From 5983c08379dbeef7be578a32e83ef364ec61f367 Mon Sep 17 00:00:00 2001 From: paschal533 Date: Mon, 9 Jun 2025 01:20:43 +0100 Subject: [PATCH] feat: Add py-libp2p to rust-libp2p interoperability tests --- tests/interop/rust_libp2p/README.md | 166 +++++++ tests/interop/rust_libp2p/py_node/ping.py | 427 ++++++++++++++++++ .../interop/rust_libp2p/rust_node/Cargo.toml | 18 + tests/interop/rust_libp2p/rust_node/README.md | 30 ++ .../interop/rust_libp2p/rust_node/src/main.rs | 68 +++ .../scripts/run_py_to_rust_test.ps1 | 44 ++ .../scripts/run_rust_to_py_test.ps1 | 78 ++++ 7 files changed, 831 insertions(+) create mode 100644 tests/interop/rust_libp2p/README.md create mode 100644 tests/interop/rust_libp2p/py_node/ping.py create mode 100644 tests/interop/rust_libp2p/rust_node/Cargo.toml create mode 100644 tests/interop/rust_libp2p/rust_node/README.md create mode 100644 tests/interop/rust_libp2p/rust_node/src/main.rs create mode 100644 tests/interop/rust_libp2p/scripts/run_py_to_rust_test.ps1 create mode 100644 tests/interop/rust_libp2p/scripts/run_rust_to_py_test.ps1 diff --git a/tests/interop/rust_libp2p/README.md b/tests/interop/rust_libp2p/README.md new file mode 100644 index 00000000..d8b15da1 --- /dev/null +++ b/tests/interop/rust_libp2p/README.md @@ -0,0 +1,166 @@ +# libp2p Interoperability Tests + +This directory contains interoperability tests between py-libp2p and rust-libp2p implementations, focusing on the ping protocol to verify core compatibility. + +## Overview + +The tests verify the following libp2p components work correctly between implementations: + +- **Transport Layer**: TCP connection establishment +- **Security Layer**: Noise encryption protocol +- **Stream Multiplexing**: Yamux multiplexer compatibility +- **Protocol Negotiation**: Multistream-select protocol selection +- **Application Protocol**: Ping protocol (`/ipfs/ping/1.0.0`) + +## Test Structure + +``` +├── py_node/ +│ └── ping.py # Python libp2p ping client/server +├── rust_node/ +│ ├── src/main.rs # Rust libp2p ping client/server +│ └── Cargo.toml +└── scripts/ + ├── run_py_to_rust_test.ps1 # Test: Python client → Rust server + └── run_rust_to_py_test.ps1 # Test: Rust client → Python server +``` + +## Prerequisites + +### Python Environment +```bash +# Install py-libp2p and dependencies +pip install . +``` + +### Rust Environment +```bash +# Ensure Rust is installed +rustc --version +cargo --version + +# Dependencies are defined in rust_node/Cargo.toml +``` + +## Running Tests + +### Test 1: Rust Client → Python Server + +This test starts a Python server and connects with a Rust client: + +```powershell +# Run the automated test +.\scripts\run_rust_to_py_test.ps1 + +# Or with custom parameters +.\scripts\run_rust_to_py_test.ps1 -Port 9000 -PingCount 10 +``` + +**Manual steps:** +1. Start Python server: `python py_node/ping.py server --port 8000` +2. Note the Peer ID from server output +3. Run Rust client: `cargo run --manifest-path rust_node/Cargo.toml -- /ip4/127.0.0.1/tcp/8000/p2p/` + +### Test 2: Python Client → Rust Server + +This test starts a Rust server and connects with a Python client: + +```powershell +# Run the automated test (requires manual intervention) +.\scripts\run_py_to_rust_test.ps1 + +# Follow the on-screen instructions to complete the test +``` + +**Manual steps:** +1. Start Rust server: `cargo run --manifest-path rust_node/Cargo.toml` +2. Note the Peer ID and port from server output +3. Run Python client: `python py_node/ping.py client /ip4/127.0.0.1/tcp//p2p/ --count 5` + +## Expected Behavior + +### Successful Test Output + +**Python Server Logs:** +``` +[INFO] Starting py-libp2p ping server... +[INFO] Peer ID: QmYourPeerIdHere +[INFO] Listening: /ip4/0.0.0.0/tcp/8000 +[INFO] New ping stream opened by 12D3KooW... +[PING 1] Received ping from 12D3KooW...: 32 bytes +[PING 1] Echoed ping back to 12D3KooW... +``` + +**Rust Client Logs:** +``` +Local peer ID: 12D3KooW... +Listening on "/ip4/0.0.0.0/tcp/54321" +Dialed /ip4/127.0.0.1/tcp/8000/p2p/QmYourPeerIdHere +Behaviour(Event { peer: QmYourPeerIdHere, result: Ok(Pong) }) +``` + +### Performance Metrics + +The tests measure: +- **Connection Establishment Time**: Time to establish secure connection +- **Round-Trip Time (RTT)**: Latency for ping/pong exchanges +- **Success Rate**: Percentage of successful ping attempts +- **Protocol Negotiation**: Successful selection of `/ipfs/ping/1.0.0` + +## Troubleshooting + +### Common Issues + +1. **Protocol Mismatch**: Ensure both implementations use the same protocol ID + - Python: `/ipfs/ping/1.0.0` + - Rust: `/ipfs/ping/1.0.0` (default ping protocol) + +2. **Connection Timeout**: + - Check firewall settings + - Verify correct IP addresses and ports + - Ensure both peers are running + +3. **Noise Encryption Errors**: + - Verify cryptography library versions + - Check that both implementations support the same Noise variants + +4. **Yamux Multiplexing Issues**: + - Confirm Yamux protocol versions match + - Check stream handling implementation + +### Debug Logging + +Enable detailed logging: + +**Python:** +```bash +# Logs are automatically written to ping_debug.log +tail -f ping_debug.log +``` + +**Rust:** +```bash +# Set environment variable for detailed logs +$env:RUST_LOG="debug" +cargo run --manifest-path rust_node/Cargo.toml +``` + +## Interoperability Checklist + +- [ ] TCP transport connection establishment +- [ ] Noise encryption handshake +- [ ] Yamux stream multiplexing +- [ ] Multistream protocol negotiation +- [ ] Ping protocol payload exchange (32 bytes) +- [ ] Proper connection cleanup +- [ ] Error handling and timeouts +- [ ] Performance metrics collection + +## Contributing + +When adding new tests: + +1. Follow the existing pattern for client/server implementations +2. Add appropriate error handling and logging +3. Update this README with new test procedures +4. Ensure tests clean up resources properly diff --git a/tests/interop/rust_libp2p/py_node/ping.py b/tests/interop/rust_libp2p/py_node/ping.py new file mode 100644 index 00000000..faa9636d --- /dev/null +++ b/tests/interop/rust_libp2p/py_node/ping.py @@ -0,0 +1,427 @@ +import argparse +import logging + +from cryptography.hazmat.primitives.asymmetric import ( + x25519, +) +import multiaddr +import trio + +from libp2p import ( + generate_new_rsa_identity, + new_host, +) +from libp2p.custom_types import ( + TProtocol, +) +from libp2p.network.stream.net_stream import ( + INetStream, +) +from libp2p.peer.peerinfo import ( + info_from_p2p_addr, +) +from libp2p.security.noise.transport import Transport as NoiseTransport +from libp2p.stream_muxer.yamux.yamux import ( + Yamux, +) +from libp2p.stream_muxer.yamux.yamux import PROTOCOL_ID as YAMUX_PROTOCOL_ID + +# Configure detailed logging +logging.basicConfig( + level=logging.DEBUG, + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=[ + logging.StreamHandler(), + logging.FileHandler("ping_debug.log", mode="w", encoding="utf-8"), + ], +) + +# Standard libp2p ping protocol - this is what rust-libp2p uses by default +PING_PROTOCOL_ID = TProtocol("/ipfs/ping/1.0.0") +PING_LENGTH = 32 +RESP_TIMEOUT = 60 + + +async def handle_ping(stream: INetStream) -> None: + """Handle incoming ping requests from rust-libp2p clients""" + peer_id = stream.muxed_conn.peer_id + print(f"[INFO] New ping stream opened by {peer_id}") + logging.info(f"Ping handler called for peer {peer_id}") + + ping_count = 0 + + try: + while True: + try: + print(f"[INFO] Waiting for ping data from {peer_id}...") + logging.debug(f"Stream state: {stream}") + data = await stream.read(PING_LENGTH) + + if not data: + print( + f"[INFO] No data received," + f" connection likely closed by {peer_id}" + ) + logging.debug("No data received, stream closed") + break + + if len(data) == 0: + print(f"[INFO] Empty data received, connection closed by {peer_id}") + logging.debug("Empty data received") + break + + ping_count += 1 + print( + f"[PING {ping_count}] Received ping from {peer_id}:" + f" {len(data)} bytes" + ) + logging.debug(f"Ping data: {data.hex()}") + + # Echo the data back (this is what ping protocol does) + await stream.write(data) + print(f"[PING {ping_count}] Echoed ping back to {peer_id}") + + except Exception as e: + print(f"[ERROR] Error in ping loop with {peer_id}: {e}") + logging.exception("Ping loop error") + break + + except Exception as e: + print(f"[ERROR] Error handling ping from {peer_id}: {e}") + logging.exception("Ping handler error") + finally: + try: + print(f"[INFO] Closing ping stream with {peer_id}") + await stream.close() + except Exception as e: + logging.debug(f"Error closing stream: {e}") + + print(f"[INFO] Ping session completed with {peer_id} ({ping_count} pings)") + + +async def send_ping_sequence(stream: INetStream, count: int = 5) -> None: + """Send a sequence of pings compatible with rust-libp2p.""" + peer_id = stream.muxed_conn.peer_id + print(f"[INFO] Starting ping sequence to {peer_id} ({count} pings)") + + import os + import time + + rtts = [] + + for i in range(1, count + 1): + try: + # Generate random 32-byte payload as per ping protocol spec + payload = os.urandom(PING_LENGTH) + print(f"[PING {i}/{count}] Sending ping to {peer_id}") + logging.debug(f"Sending payload: {payload.hex()}") + start_time = time.time() + + await stream.write(payload) + + with trio.fail_after(RESP_TIMEOUT): + response = await stream.read(PING_LENGTH) + + end_time = time.time() + rtt = (end_time - start_time) * 1000 + + if ( + response + and len(response) >= PING_LENGTH + and response[:PING_LENGTH] == payload + ): + rtts.append(rtt) + print(f"[PING {i}] Successful! RTT: {rtt:.2f}ms") + else: + print(f"[ERROR] Ping {i} failed: response mismatch or incomplete") + if response: + logging.debug(f"Expected: {payload.hex()}") + logging.debug(f"Received: {response.hex()}") + + if i < count: + await trio.sleep(1) + + except trio.TooSlowError: + print(f"[ERROR] Ping {i} timed out after {RESP_TIMEOUT}s") + except Exception as e: + print(f"[ERROR] Ping {i} failed: {e}") + logging.exception(f"Ping {i} error") + + # Print statistics + if rtts: + avg_rtt = sum(rtts) / len(rtts) + min_rtt = min(rtts) + max_rtt = max(rtts) # Fixed typo: was max_rtts + success_count = len(rtts) + loss_rate = ((count - success_count) / count) * 100 + + print(f"\n[STATS] Ping Statistics:") + print( + f" Packets: Sent={count}, Received={success_count}," + f" Lost={count - success_count}" + ) + print(f" Loss rate: {loss_rate:.1f}%") + print( + f" RTT: min={min_rtt:.2f}ms, avg={avg_rtt:.2f}ms," + f" max={max_rtt:.2f}ms" + ) + else: + print(f"\n[STATS] All pings failed ({count} attempts)") + + +def create_noise_keypair(): + """Create a Noise protocol keypair for secure communication""" + try: + x25519_private_key = x25519.X25519PrivateKey.generate() + + class NoisePrivateKey: + def __init__(self, key): + self._key = key + + def to_bytes(self): + return self._key.private_bytes_raw() + + def public_key(self): + return NoisePublicKey(self._key.public_key()) + + def get_public_key(self): + return NoisePublicKey(self._key.public_key()) + + class NoisePublicKey: + def __init__(self, key): + self._key = key + + def to_bytes(self): + return self._key.public_bytes_raw() + + return NoisePrivateKey(x25519_private_key) + except Exception as e: + logging.error(f"Failed to create Noise keypair: {e}") + return None + + +async def run_server(port: int) -> None: + """Run ping server that accepts connections from rust-libp2p clients.""" + listen_addr = multiaddr.Multiaddr(f"/ip4/0.0.0.0/tcp/{port}") + + key_pair = generate_new_rsa_identity() + logging.debug("Generated RSA keypair") + + noise_privkey = create_noise_keypair() + if not noise_privkey: + print("[ERROR] Failed to create Noise keypair") + return + logging.debug("Generated Noise keypair") + + noise_transport = NoiseTransport(key_pair, noise_privkey=noise_privkey) + logging.debug(f"Noise transport initialized: {noise_transport}") + sec_opt = {TProtocol("/noise"): noise_transport} + muxer_opt = {TProtocol(YAMUX_PROTOCOL_ID): Yamux} + + logging.info(f"Using muxer: {muxer_opt}") + + host = new_host(key_pair=key_pair, sec_opt=sec_opt, muxer_opt=muxer_opt) + + print("[INFO] Starting py-libp2p ping server...") + + async with host.run(listen_addrs=[listen_addr]): + print(f"[INFO] Registering ping handler for protocol: {PING_PROTOCOL_ID}") + host.set_stream_handler(PING_PROTOCOL_ID, handle_ping) + + # Also register alternative protocol IDs for better compatibility + alt_protocols = [ + TProtocol("/ping/1.0.0"), + TProtocol("/libp2p/ping/1.0.0"), + ] + + for alt_proto in alt_protocols: + print(f"[INFO] Also registering handler for: {alt_proto}") + host.set_stream_handler(alt_proto, handle_ping) + + print("[INFO] Server started successfully!") + print(f"[INFO] Peer ID: {host.get_id()}") + print(f"[INFO] Listening: /ip4/0.0.0.0/tcp/{port}") + print(f"[INFO] Primary Protocol: {PING_PROTOCOL_ID}") + print(f"[INFO] Security: Noise encryption") + print(f"[INFO] Muxer: Yamux stream multiplexing") + + print("\n[INFO] Registered protocols:") + print(f" - {PING_PROTOCOL_ID}") + for proto in alt_protocols: + print(f" - {proto}") + + peer_id = host.get_id() + print("\n[TEST] Test with rust-libp2p:") + print(f" cargo run -- /ip4/127.0.0.1/tcp/{port}/p2p/{peer_id}") + + print("\n[TEST] Test with py-libp2p:") + print(f" python ping.py client /ip4/127.0.0.1/tcp/{port}/p2p/{peer_id}") + + print("\n[INFO] Waiting for connections...") + print("Press Ctrl+C to exit") + + await trio.sleep_forever() + + +async def run_client(destination: str, count: int = 5) -> None: + """Run ping client to test connectivity with another peer.""" + listen_addr = multiaddr.Multiaddr("/ip4/0.0.0.0/tcp/0") + + key_pair = generate_new_rsa_identity() + logging.debug("Generated RSA keypair") + + noise_privkey = create_noise_keypair() + if not noise_privkey: + print("[ERROR] Failed to create Noise keypair") + return 1 + logging.debug("Generated Noise keypair") + + noise_transport = NoiseTransport(key_pair, noise_privkey=noise_privkey) + logging.debug(f"Noise transport initialized: {noise_transport}") + sec_opt = {TProtocol("/noise"): noise_transport} + muxer_opt = {TProtocol(YAMUX_PROTOCOL_ID): Yamux} + + logging.info(f"Using muxer: {muxer_opt}") + + host = new_host(key_pair=key_pair, sec_opt=sec_opt, muxer_opt=muxer_opt) + + print("[INFO] Starting py-libp2p ping client...") + + async with host.run(listen_addrs=[listen_addr]): + print(f"[INFO] Our Peer ID: {host.get_id()}") + print(f"[INFO] Target: {destination}") + print("[INFO] Security: Noise encryption") + print("[INFO] Muxer: Yamux stream multiplexing") + + try: + maddr = multiaddr.Multiaddr(destination) + info = info_from_p2p_addr(maddr) + target_peer_id = info.peer_id + + print(f"[INFO] Target Peer ID: {target_peer_id}") + print("[INFO] Connecting to peer...") + + await host.connect(info) + print("[INFO] Connection established!") + + # Try protocols in order of preference + # Start with the standard libp2p ping protocol + protocols_to_try = [ + PING_PROTOCOL_ID, # /ipfs/ping/1.0.0 - standard protocol + TProtocol("/ping/1.0.0"), # Alternative + TProtocol("/libp2p/ping/1.0.0"), # Another alternative + ] + + stream = None + + for proto in protocols_to_try: + try: + print(f"[INFO] Trying to open stream with protocol: {proto}") + stream = await host.new_stream(target_peer_id, [proto]) + print(f"[INFO] Stream opened with protocol: {proto}") + break + except Exception as e: + print(f"[ERROR] Failed to open stream with {proto}: {e}") + logging.debug(f"Protocol {proto} failed: {e}") + continue + + if not stream: + print("[ERROR] Failed to open stream with any ping protocol") + print("[ERROR] Ensure the target peer supports one of these protocols:") + for proto in protocols_to_try: + print(f"[ERROR] - {proto}") + return 1 + + await send_ping_sequence(stream, count) + + await stream.close() + print("[INFO] Stream closed successfully") + + except Exception as e: + print(f"[ERROR] Client error: {e}") + logging.exception("Client error") + import traceback + traceback.print_exc() + return 1 + + print("\n[INFO] Client stopped") + return 0 + + +def main() -> None: + """Main function with argument parsing.""" + description = """ + py-libp2p ping tool for interoperability testing with rust-libp2p. + Uses Noise encryption and Yamux multiplexing for compatibility. + + Server mode: Listens for ping requests from rust-libp2p or py-libp2p clients. + Client mode: Sends ping requests to rust-libp2p or py-libp2p servers. + + The tool implements the standard libp2p ping protocol (/ipfs/ping/1.0.0) + which exchanges 32-byte random payloads and measures round-trip time. + """ + + example_maddr = ( + "/ip4/127.0.0.1/tcp/8000/p2p/QmQn4SwGkDZKkUEpBRBvTmheQycxAHJUNmVEnjA2v1qe8Q" + ) + + parser = argparse.ArgumentParser( + description=description, + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=f""" +Examples: + python ping.py server # Start server on port 8000 + python ping.py server --port 9000 # Start server on port 9000 + python ping.py client {example_maddr} + python ping.py client {example_maddr} --count 10 + +Protocols supported: + - /ipfs/ping/1.0.0 (primary, rust-libp2p default) + - /ping/1.0.0 (alternative) + - /libp2p/ping/1.0.0 (alternative) + """, + ) + + subparsers = parser.add_subparsers(dest="mode", help="Operation mode") + + server_parser = subparsers.add_parser("server", help="Run as ping server") + server_parser.add_argument( + "--port", "-p", type=int, default=8000, help="Port to listen on (default: 8000)" + ) + + client_parser = subparsers.add_parser("client", help="Run as ping client") + client_parser.add_argument("destination", help="Target peer multiaddr") + client_parser.add_argument( + "--count", + "-c", + type=int, + default=5, + help="Number of pings to send (default: 5)", + ) + + args = parser.parse_args() + + if not args.mode: + parser.print_help() + return 1 + + try: + if args.mode == "server": + trio.run(run_server, args.port) + elif args.mode == "client": + return trio.run(run_client, args.destination, args.count) + except KeyboardInterrupt: + print("\n[INFO] Goodbye!") + return 0 + except Exception as e: + print(f"[ERROR] Fatal error: {e}") + logging.exception("Fatal error") + import traceback + traceback.print_exc() + return 1 + + return 0 + + +if __name__ == "__main__": + exit(main()) \ No newline at end of file diff --git a/tests/interop/rust_libp2p/rust_node/Cargo.toml b/tests/interop/rust_libp2p/rust_node/Cargo.toml new file mode 100644 index 00000000..63d54fb6 --- /dev/null +++ b/tests/interop/rust_libp2p/rust_node/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ping-example" +version = "0.1.0" +edition.workspace = true +publish = false +license = "MIT" + +[package.metadata.release] +release = false + +[dependencies] +futures = { workspace = true } +libp2p = { path = "../../libp2p", features = ["noise", "ping", "tcp", "tokio", "yamux", "rsa"] } +tokio = { workspace = true, features = ["full"] } +tracing-subscriber = { workspace = true, features = ["env-filter"] } + +[lints] +workspace = true diff --git a/tests/interop/rust_libp2p/rust_node/README.md b/tests/interop/rust_libp2p/rust_node/README.md new file mode 100644 index 00000000..9abc8b71 --- /dev/null +++ b/tests/interop/rust_libp2p/rust_node/README.md @@ -0,0 +1,30 @@ +## Description + +The ping example showcases how to create a network of nodes that establish connections, negotiate the ping protocol, and ping each other. + +## Usage + +To run the example, follow these steps: + +1. In a first terminal window, run the following command: + + ```sh + cargo run + ``` + + This command starts a node and prints the `PeerId` and the listening addresses, such as `Listening on "/ip4/0.0.0.0/tcp/24915"`. + +2. In a second terminal window, start a new instance of the example with the following command: + + ```sh + cargo run -- /ip4/127.0.0.1/tcp/24915 + ``` + + Replace `/ip4/127.0.0.1/tcp/24915` with the listen address of the first node obtained from the first terminal window. + +3. The two nodes will establish a connection, negotiate the ping protocol, and begin pinging each other. + +## Conclusion + +The ping example demonstrates the basic usage of **libp2p** to create a simple p2p network and implement a ping protocol. +By running multiple nodes and observing the ping behavior, users can gain insights into how **libp2p** facilitates communication and interaction between peers. diff --git a/tests/interop/rust_libp2p/rust_node/src/main.rs b/tests/interop/rust_libp2p/rust_node/src/main.rs new file mode 100644 index 00000000..298e392b --- /dev/null +++ b/tests/interop/rust_libp2p/rust_node/src/main.rs @@ -0,0 +1,68 @@ +// Copyright 2018 Parity Technologies (UK) Ltd. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#![doc = include_str!("../README.md")] + +use std::{error::Error, time::Duration}; + +use futures::prelude::*; +use libp2p::{noise, ping, swarm::SwarmEvent, tcp, yamux, Multiaddr}; +use tracing_subscriber::EnvFilter; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let _ = tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .try_init(); + + let mut swarm = libp2p::SwarmBuilder::with_new_identity() + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + )? + .with_behaviour(|_| ping::Behaviour::default())? + .with_swarm_config(|cfg| cfg.with_idle_connection_timeout(Duration::from_secs(u64::MAX))) + .build(); + + // Print the peer ID + println!("Local peer ID: {}", swarm.local_peer_id()); + + // Tell the swarm to listen on all interfaces and a random, OS-assigned + // port. + swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; + + // Dial the peer identified by the multi-address given as the second + // command-line argument, if any. + if let Some(addr) = std::env::args().nth(1) { + let remote: Multiaddr = addr.parse()?; + swarm.dial(remote)?; + println!("Dialed {addr}") + } + + loop { + match swarm.select_next_some().await { + SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"), + SwarmEvent::Behaviour(event) => println!("{event:?}"), + _ => {} + } + } +} \ No newline at end of file diff --git a/tests/interop/rust_libp2p/scripts/run_py_to_rust_test.ps1 b/tests/interop/rust_libp2p/scripts/run_py_to_rust_test.ps1 new file mode 100644 index 00000000..b636ca3a --- /dev/null +++ b/tests/interop/rust_libp2p/scripts/run_py_to_rust_test.ps1 @@ -0,0 +1,44 @@ +# scripts/run_py_to_rust_test.ps1 +# Test script for py-libp2p client connecting to rust-libp2p server + +param( + [int]$PingCount = 5, + [int]$TimeoutSeconds = 30 +) + +Write-Host "=== py-libp2p to rust-libp2p Interop Test ===" -ForegroundColor Cyan +Write-Host "Starting rust-libp2p server..." -ForegroundColor Yellow + +# Start rust server in background +$rustProcess = Start-Process -FilePath "cargo" -ArgumentList "run" -WorkingDirectory "rust_node" -PassThru -WindowStyle Hidden + +# Wait a moment for server to start +Start-Sleep -Seconds 3 + +try { + # Get the rust server's listening address from its output + # For now, we'll assume it's listening on a predictable port + # In a real scenario, you'd parse the server output to get the actual address + + Write-Host "Waiting for rust server to start..." -ForegroundColor Yellow + Start-Sleep -Seconds 5 + + # Try to find the server's peer ID and port from netstat or process output + # For this test, we'll need to manually check the rust server output + Write-Host "Please check the rust server output for its Peer ID and port" -ForegroundColor Red + Write-Host "Then run the Python client manually with:" -ForegroundColor Yellow + Write-Host "python py_node/ping.py client /ip4/127.0.0.1/tcp//p2p/ --count $PingCount" -ForegroundColor Green + + # Keep the server running + Write-Host "Press any key to stop the test..." -ForegroundColor Cyan + $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") + +} finally { + # Clean up + Write-Host "Stopping rust server..." -ForegroundColor Yellow + if ($rustProcess -and !$rustProcess.HasExited) { + $rustProcess.Kill() + $rustProcess.WaitForExit(5000) + } + Write-Host "Test completed." -ForegroundColor Green +} \ No newline at end of file diff --git a/tests/interop/rust_libp2p/scripts/run_rust_to_py_test.ps1 b/tests/interop/rust_libp2p/scripts/run_rust_to_py_test.ps1 new file mode 100644 index 00000000..d4604a19 --- /dev/null +++ b/tests/interop/rust_libp2p/scripts/run_rust_to_py_test.ps1 @@ -0,0 +1,78 @@ +# scripts/run_rust_to_py_test.ps1 +# Test script for rust-libp2p client connecting to py-libp2p server + +param( + [int]$Port = 8000, + [int]$PingCount = 5, + [int]$TimeoutSeconds = 30 +) + +Write-Host "=== rust-libp2p to py-libp2p Interop Test ===" -ForegroundColor Cyan +Write-Host "Starting py-libp2p server on port $Port..." -ForegroundColor Yellow + +# Start Python server in background +$pyProcess = Start-Process -FilePath "python" -ArgumentList "py_node/ping.py", "server", "--port", $Port -PassThru -RedirectStandardOutput "py_server_output.txt" -RedirectStandardError "py_server_error.txt" + +# Wait for server to start +Start-Sleep -Seconds 5 + +try { + # Read the server output to get peer ID + $maxWaitTime = 10 + $waited = 0 + $peerID = $null + + while ($waited -lt $maxWaitTime -and !$peerID) { + if (Test-Path "py_server_output.txt") { + $output = Get-Content "py_server_output.txt" -Raw + if ($output -match "Peer ID: ([\w\d]+)") { + $peerID = $matches[1] + break + } + } + Start-Sleep -Seconds 1 + $waited++ + } + + if (!$peerID) { + Write-Host "Could not extract Peer ID from Python server output" -ForegroundColor Red + Write-Host "Server output:" -ForegroundColor Yellow + if (Test-Path "py_server_output.txt") { + Get-Content "py_server_output.txt" + } + if (Test-Path "py_server_error.txt") { + Write-Host "Server errors:" -ForegroundColor Red + Get-Content "py_server_error.txt" + } + return + } + + $multiaddr = "/ip4/127.0.0.1/tcp/$Port/p2p/$peerID" + Write-Host "Python server started with Peer ID: $peerID" -ForegroundColor Green + Write-Host "Full address: $multiaddr" -ForegroundColor Green + + Write-Host "Starting rust client..." -ForegroundColor Yellow + + # Run rust client + $rustResult = Start-Process -FilePath "cargo" -ArgumentList "run", "--", $multiaddr -WorkingDirectory "rust_node" -Wait -PassThru -NoNewWindow + + if ($rustResult.ExitCode -eq 0) { + Write-Host "Rust client completed successfully!" -ForegroundColor Green + } else { + Write-Host "Rust client failed with exit code: $($rustResult.ExitCode)" -ForegroundColor Red + } + +} finally { + # Clean up + Write-Host "Stopping Python server..." -ForegroundColor Yellow + if ($pyProcess -and !$pyProcess.HasExited) { + $pyProcess.Kill() + $pyProcess.WaitForExit(5000) + } + + # Clean up output files + if (Test-Path "py_server_output.txt") { Remove-Item "py_server_output.txt" } + if (Test-Path "py_server_error.txt") { Remove-Item "py_server_error.txt" } + + Write-Host "Test completed." -ForegroundColor Green +} \ No newline at end of file