fix: added identify push to setup.py

This commit is contained in:
acul71
2025-04-21 21:30:42 +02:00
committed by Paul Robinson
parent e752629212
commit 91beff5e63
4 changed files with 252 additions and 10 deletions

View File

@ -35,10 +35,10 @@ from libp2p.peer.peerinfo import (
)
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
# logging.basicConfig(
# level=logging.DEBUG,
# format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
# )
logger = logging.getLogger(__name__)
@ -119,3 +119,9 @@ async def main() -> None:
if __name__ == "__main__":
trio.run(main)
# Non-async entry point for console script
def run_main():
"""Non-async entry point for the console script."""
trio.run(main)

View File

@ -23,11 +23,17 @@ import logging
import sys
import multiaddr
from multiaddr import (
Multiaddr,
)
import trio
from libp2p import (
new_host,
)
from libp2p.abc import (
INetStream,
)
from libp2p.crypto.secp256k1 import (
create_new_key_pair,
)
@ -35,6 +41,9 @@ from libp2p.identity.identify import (
identify_handler_for,
)
from libp2p.identity.identify import ID as ID_IDENTIFY
from libp2p.identity.identify.pb.identify_pb2 import (
Identify,
)
from libp2p.identity.identify_push import (
identify_push_handler_for,
push_identify_to_peer,
@ -45,16 +54,88 @@ from libp2p.peer.peerinfo import (
)
# Configure logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
# logging.basicConfig(
# level=logging.DEBUG,
# format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
# )
logger = logging.getLogger("libp2p.identity.identify-push-example")
# Default port configuration
DEFAULT_PORT = 8888
def custom_identify_push_handler_for(host):
"""
Create a custom handler for the identify/push protocol that logs and prints
the identity information received from the dialer.
"""
async def handle_identify_push(stream: INetStream) -> None:
peer_id = stream.muxed_conn.peer_id
try:
# Read the identify message from the stream
data = await stream.read()
identify_msg = Identify()
identify_msg.ParseFromString(data)
# Log and print the identify information
logger.info("Received identify/push from peer %s", peer_id)
print(f"\n==== Received identify/push from peer {peer_id} ====")
if identify_msg.HasField("protocol_version"):
logger.info(" Protocol Version: %s", identify_msg.protocol_version)
print(f" Protocol Version: {identify_msg.protocol_version}")
if identify_msg.HasField("agent_version"):
logger.info(" Agent Version: %s", identify_msg.agent_version)
print(f" Agent Version: {identify_msg.agent_version}")
if identify_msg.HasField("public_key"):
logger.info(
" Public Key: %s", identify_msg.public_key.hex()[:16] + "..."
)
print(f" Public Key: {identify_msg.public_key.hex()[:16]}...")
if identify_msg.listen_addrs:
addrs = [Multiaddr(addr) for addr in identify_msg.listen_addrs]
logger.info(" Listen Addresses: %s", addrs)
print(" Listen Addresses:")
for addr in addrs:
print(f" - {addr}")
if identify_msg.HasField("observed_addr") and identify_msg.observed_addr:
observed_addr = Multiaddr(identify_msg.observed_addr)
logger.info(" Observed Address: %s", observed_addr)
print(f" Observed Address: {observed_addr}")
if identify_msg.protocols:
logger.info(" Protocols: %s", identify_msg.protocols)
print(" Protocols:")
for protocol in identify_msg.protocols:
print(f" - {protocol}")
# Update the peerstore with the new information as usual
peerstore = host.get_peerstore()
from libp2p.identity.identify_push.identify_push import (
_update_peerstore_from_identify,
)
await _update_peerstore_from_identify(peerstore, peer_id, identify_msg)
logger.info("Successfully processed identify/push from peer %s", peer_id)
print(f"\nSuccessfully processed identify/push from peer {peer_id}")
except Exception as e:
logger.error("Error processing identify/push from %s: %s", peer_id, e)
print(f"\nError processing identify/push from {peer_id}: {e}")
finally:
# Close the stream after processing
await stream.close()
return handle_identify_push
async def run_listener(port: int) -> None:
"""Run a host in listener mode."""
print(f"\n==== Starting Identify-Push Listener on port {port} ====\n")
@ -67,7 +148,7 @@ async def run_listener(port: int) -> None:
# Set up the identify and identify/push handlers
host.set_stream_handler(ID_IDENTIFY, identify_handler_for(host))
host.set_stream_handler(ID_IDENTIFY_PUSH, identify_push_handler_for(host))
host.set_stream_handler(ID_IDENTIFY_PUSH, custom_identify_push_handler_for(host))
# Start listening
listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}")
@ -84,7 +165,7 @@ async def run_listener(port: int) -> None:
print(f"Peer ID: {host.get_id().pretty()}")
print("\nRun dialer with command:")
print(f"python identify_push_listener_dialer.py -d {addr}")
print(f"identify-push-listener-dialer-demo -d {addr}")
print("\nWaiting for incoming connections... (Ctrl+C to exit)")
# Keep running until interrupted

View File

@ -108,6 +108,8 @@ setup(
"echo-demo=examples.echo.echo:main",
"ping-demo=examples.ping.ping:main",
"identify-demo=examples.identify.identify:main",
"identify-push-demo=examples.identify_push.identify_push_demo:run_main",
"identify-push-listener-dialer-demo=examples.identify_push.identify_push_listener_dialer:main",
"pubsub-demo=examples.pubsub.pubsub:main",
],
},

View File

@ -14,6 +14,7 @@ from libp2p.identity.identify_push.identify_push import (
_update_peerstore_from_identify,
identify_push_handler_for,
push_identify_to_peer,
push_identify_to_peers,
)
from tests.utils.factories import (
host_pair_factory,
@ -24,6 +25,16 @@ logger = logging.getLogger("libp2p.identity.identify-push-test")
@pytest.mark.trio
async def test_identify_push_protocol(security_protocol):
"""
Test the basic functionality of the identify/push protocol.
This test verifies that when host_a pushes identify information to host_b:
1. The information is correctly received and processed
2. The peerstore of host_b is updated with host_a's peer ID
3. The peerstore contains all of host_a's addresses
4. The peerstore contains all of host_a's supported protocols
5. The public key in the peerstore matches host_a's public key
"""
async with host_pair_factory(security_protocol=security_protocol) as (
host_a,
host_b,
@ -72,6 +83,17 @@ async def test_identify_push_protocol(security_protocol):
@pytest.mark.trio
async def test_identify_push_handler(security_protocol):
"""
Test the identify_push_handler_for function specifically.
This test focuses on verifying that the handler function correctly:
1. Receives the identify message
2. Updates the peerstore with the peer information
3. Processes all fields of the identify message (addresses, protocols, public key)
Note: This test is similar to test_identify_push_protocol but specifically
focuses on the handler function's behavior.
"""
async with host_pair_factory(security_protocol=security_protocol) as (
host_a,
host_b,
@ -120,6 +142,16 @@ async def test_identify_push_handler(security_protocol):
@pytest.mark.trio
async def test_identify_push_to_peers(security_protocol):
"""
Test the push_identify_to_peers function to broadcast identity to multiple peers.
This test verifies that:
1. Host_a can push identify information to multiple peers simultaneously
2. The identify information is correctly received by all connected peers
3. Both host_b and host_c have their peerstores updated with host_a's information
This tests the broadcasting capability of the identify/push protocol.
"""
# Create three hosts
async with host_pair_factory(security_protocol=security_protocol) as (
host_a,
@ -178,8 +210,116 @@ async def test_identify_push_to_peers(security_protocol):
assert peer_id_a in peerstore_c.peer_ids()
@pytest.mark.trio
async def test_push_identify_to_peers_with_explicit_params(security_protocol):
"""
Test the push_identify_to_peers function with explicit parameters.
This test verifies that:
1. The function correctly handles an explicitly provided set of peer IDs
2. The function correctly uses the provided observed_multiaddr
3. The identify information is only pushed to the specified peers
4. The observed address is correctly included in the identify message
This test ensures all parameters of push_identify_to_peers are properly tested.
"""
import multiaddr
from libp2p import (
new_host,
)
from libp2p.crypto.secp256k1 import (
create_new_key_pair,
)
from libp2p.peer.peerinfo import (
info_from_p2p_addr,
)
# Create four hosts to thoroughly test selective pushing
async with host_pair_factory(security_protocol=security_protocol) as (
host_a,
host_b,
):
# Create two additional hosts
key_pair_c = create_new_key_pair()
host_c = new_host(key_pair=key_pair_c)
key_pair_d = create_new_key_pair()
host_d = new_host(key_pair=key_pair_d)
# Set up the identify/push handlers for all hosts
host_b.set_stream_handler(ID_PUSH, identify_push_handler_for(host_b))
host_c.set_stream_handler(ID_PUSH, identify_push_handler_for(host_c))
host_d.set_stream_handler(ID_PUSH, identify_push_handler_for(host_d))
# Start listening on random ports
listen_addr_c = multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/0")
listen_addr_d = multiaddr.Multiaddr("/ip4/127.0.0.1/tcp/0")
async with host_c.run([listen_addr_c]), host_d.run([listen_addr_d]):
# Connect all hosts to host_a
await host_c.connect(info_from_p2p_addr(host_a.get_addrs()[0]))
await host_d.connect(info_from_p2p_addr(host_a.get_addrs()[0]))
# Create a specific observed multiaddr for the test
observed_addr = multiaddr.Multiaddr("/ip4/192.0.2.1/tcp/1234")
# Only push to hosts B and C (not D)
selected_peers = {host_b.get_id(), host_c.get_id()}
# Push identify information from host_a to selected peers with observed addr
await push_identify_to_peers(
host=host_a, peer_ids=selected_peers, observed_multiaddr=observed_addr
)
# Wait a bit for the push to complete
await trio.sleep(0.1)
# Check that host_b's and host_c's peerstores have been updated
peerstore_b = host_b.get_peerstore()
peerstore_c = host_c.get_peerstore()
peerstore_d = host_d.get_peerstore()
peer_id_a = host_a.get_id()
# Hosts B and C should have peer_id_a in their peerstores
assert peer_id_a in peerstore_b.peer_ids()
assert peer_id_a in peerstore_c.peer_ids()
# Host D should NOT have peer_id_a in its peerstore from the push
# (it may still have it from the connection)
# So we check for the observed address instead, which would only be
# present from a push
# Hosts B and C should have the observed address in their peerstores
addrs_b = [str(addr) for addr in peerstore_b.addrs(peer_id_a)]
addrs_c = [str(addr) for addr in peerstore_c.addrs(peer_id_a)]
assert str(observed_addr) in addrs_b
assert str(observed_addr) in addrs_c
# If host D has addresses for peer_id_a, the observed address
# should not be there
if peer_id_a in peerstore_d.peer_ids():
addrs_d = [str(addr) for addr in peerstore_d.addrs(peer_id_a)]
assert str(observed_addr) not in addrs_d
@pytest.mark.trio
async def test_update_peerstore_from_identify(security_protocol):
"""
Test the _update_peerstore_from_identify function directly.
This test verifies that the internal function responsible for updating
the peerstore from an identify message works correctly:
1. It properly updates the peerstore with all fields from the identify message
2. The peer ID is added to the peerstore
3. All addresses are correctly stored
4. All protocols are correctly stored
5. The public key is correctly stored
This tests the low-level peerstore update mechanism used by
the identify/push protocol.
"""
async with host_pair_factory(security_protocol=security_protocol) as (
host_a,
host_b,
@ -225,6 +365,19 @@ async def test_update_peerstore_from_identify(security_protocol):
@pytest.mark.trio
async def test_partial_update_peerstore_from_identify(security_protocol):
"""
Test partial updates of the peerstore using the identify/push protocol.
This test verifies that:
1. A partial identify message (containing only some fields) correctly updates
the peerstore without affecting other existing information
2. New protocols are added to the existing set in the peerstore
3. The original protocols, addresses, and public key remain intact
4. The update is additive rather than replacing all existing data
This tests the ability of the identify/push protocol to handle incremental
or partial updates to peer information.
"""
async with host_pair_factory(security_protocol=security_protocol) as (
host_a,
host_b,