Add interoperability test for py-libp2p and js-libp2p with enhanced logging

This commit is contained in:
paschal533
2025-06-09 00:11:19 +01:00
committed by acul71
parent 505d3b2a8f
commit 1507100632
13 changed files with 1632 additions and 5 deletions

View File

@ -0,0 +1,81 @@
# py-libp2p and js-libp2p Interoperability Tests
This repository contains interoperability tests for py-libp2p and js-libp2p using the /ipfs/ping/1.0.0 protocol. The goal is to verify compatibility in stream multiplexing, protocol negotiation, ping handling, transport layer, and multiaddr parsing.
## Directory Structure
- js_node/ping.js: JavaScript implementation of a ping server and client using libp2p.
- py_node/ping.py: Python implementation of a ping server and client using py-libp2p.
- scripts/run_test.sh: Shell script to automate running the server and client for testing.
- README.md: This file.
## Prerequisites
- Python 3.8+ with `py-libp2p` and dependencies (`pip install libp2p trio cryptography multiaddr`).
- Node.js 16+ with `libp2p` dependencies (`npm install @libp2p/core @libp2p/tcp @chainsafe/libp2p-noise @chainsafe/libp2p-yamux @libp2p/ping @libp2p/identify @multiformats/multiaddr`).
- Bash shell for running `run_test.sh`.
## Running Tests
1. Change directory:
```
cd tests/interop/js_libp2p
```
2. Install dependencies:
```
For JavaScript: cd js_node && npm install && cd ...
```
3. Run the automated test:
For Linux and Mac users:
```
chmod +x scripts/run_test.sh
./scripts/run_test.sh
```
For Windows users:
```
.\scripts\run_test.ps1
```
This starts the Python server on port 8000 and runs the JavaScript client to send 5 pings.
## Debugging
- Logs are saved in py_node/py_server.log and js_node/js_client.log.
- Check for:
- Successful connection establishment.
- Protocol negotiation (/ipfs/ping/1.0.0).
- 32-byte payload echo in server logs.
- RTT and payload hex in client logs.
## Test Plan
### The test verifies:
- Stream Multiplexer Compatibility: Yamux is used and negotiates correctly.
- Multistream Protocol Negotiation: /ipfs/ping/1.0.0 is selected via multistream-select.
- Ping Protocol Handler: Handles 32-byte payloads per the libp2p ping spec.
- Transport Layer Support: TCP is used; WebSocket support is optional.
- Multiaddr Parsing: Correctly resolves multiaddr strings.
- Logging: Includes peer ID, RTT, and payload hex for debugging.
## Current Status
### Working:
- TCP transport and Noise encryption are functional.
- Yamux multiplexing is implemented in both nodes.
- Multiaddr parsing works correctly.
- Logging provides detailed debug information.
## Not Working:
- Ping protocol handler fails to complete pings (JS client reports "operation aborted").
- Potential issues with stream handling or protocol negotiation.

View File

@ -0,0 +1,4 @@
/node_modules
/package-lock.json
/dist
.log

View File

@ -0,0 +1,17 @@
# ⚠️ IMPORTANT ⚠️
# Please do not create a Pull Request for this repository
The contents of this repository are automatically synced from the parent [js-libp2p Examples Project](https://github.com/libp2p/js-libp2p-examples) so any changes made to the standalone repository will be lost after the next sync.
Please open a PR against [js-libp2p Examples](https://github.com/libp2p/js-libp2p-examples) instead.
## Contributing
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
1. Fork the [js-libp2p Examples Project](https://github.com/libp2p/js-libp2p-examples)
1. Create your Feature Branch (`git checkout -b feature/amazing-example`)
1. Commit your Changes (`git commit -a -m 'feat: add some amazing example'`)
1. Push to the Branch (`git push origin feature/amazing-example`)
1. Open a Pull Request

View File

@ -0,0 +1,19 @@
name: pull
on:
workflow_dispatch
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Pull from another repository
uses: ipfs-examples/actions-pull-directory-from-repo@main
with:
source-repo: libp2p/js-libp2p-examples
source-folder-path: examples/${{ github.event.repository.name }}
source-branch: main
target-branch: main
git-username: github-actions
git-email: github-actions@github.com

View File

@ -0,0 +1,53 @@
# @libp2p/example-chat <!-- omit in toc -->
[![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
[![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io)
[![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-examples.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-examples)
[![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p-examples/ci.yml?branch=main&style=flat-square)](https://github.com/libp2p/js-libp2p-examples/actions/workflows/ci.yml?query=branch%3Amain)
> An example chat app using libp2p
## Table of contents <!-- omit in toc -->
- [Setup](#setup)
- [Running](#running)
- [Need help?](#need-help)
- [License](#license)
- [Contribution](#contribution)
## Setup
1. Install example dependencies
```console
$ npm install
```
1. Open 2 terminal windows in the `./src` directory.
## Running
1. Run the listener in window 1, `node listener.js`
1. Run the dialer in window 2, `node dialer.js`
1. Wait until the two peers discover each other
1. Type a message in either window and hit *enter*
1. Tell yourself secrets to your hearts content!
## Need help?
- Read the [js-libp2p documentation](https://github.com/libp2p/js-libp2p/tree/main/doc)
- Check out the [js-libp2p API docs](https://libp2p.github.io/js-libp2p/)
- Check out the [general libp2p documentation](https://docs.libp2p.io) for tips, how-tos and more
- Read the [libp2p specs](https://github.com/libp2p/specs)
- Ask a question on the [js-libp2p discussion board](https://github.com/libp2p/js-libp2p/discussions)
## License
Licensed under either of
- Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT ([LICENSE-MIT](LICENSE-MIT) / <http://opensource.org/licenses/MIT>)
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

View File

@ -0,0 +1,39 @@
{
"name": "@libp2p/example-chat",
"version": "0.0.0",
"description": "An example chat app using libp2p",
"license": "Apache-2.0 OR MIT",
"homepage": "https://github.com/libp2p/js-libp2p-example-chat#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/libp2p/js-libp2p-examples.git"
},
"bugs": {
"url": "https://github.com/libp2p/js-libp2p-examples/issues"
},
"type": "module",
"scripts": {
"test": "test-node-example test/*"
},
"dependencies": {
"@chainsafe/libp2p-noise": "^16.0.0",
"@chainsafe/libp2p-yamux": "^7.0.0",
"@libp2p/identify": "^3.0.33",
"@libp2p/mdns": "^11.0.1",
"@libp2p/ping": "^2.0.33",
"@libp2p/tcp": "^10.0.0",
"@libp2p/websockets": "^9.0.0",
"@multiformats/multiaddr": "^12.3.1",
"@nodeutils/defaults-deep": "^1.1.0",
"it-length-prefixed": "^10.0.1",
"it-map": "^3.0.3",
"it-pipe": "^3.0.1",
"libp2p": "^2.0.0",
"p-defer": "^4.0.0",
"uint8arrays": "^5.1.0"
},
"devDependencies": {
"test-ipfs-example": "^1.1.0"
},
"private": true
}

View File

@ -0,0 +1,204 @@
#!/usr/bin/env node
import { createLibp2p } from 'libp2p'
import { tcp } from '@libp2p/tcp'
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { ping } from '@libp2p/ping'
import { identify } from '@libp2p/identify'
import { multiaddr } from '@multiformats/multiaddr'
async function createNode() {
return await createLibp2p({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0']
},
transports: [
tcp()
],
connectionEncrypters: [
noise()
],
streamMuxers: [
yamux()
],
services: {
// Use ipfs prefix to match py-libp2p example
ping: ping({
protocolPrefix: 'ipfs',
maxInboundStreams: 32,
maxOutboundStreams: 64,
timeout: 30000
}),
identify: identify()
},
connectionManager: {
minConnections: 0,
maxConnections: 100,
dialTimeout: 30000
}
})
}
async function runServer() {
console.log('🚀 Starting js-libp2p ping server...')
const node = await createNode()
await node.start()
console.log('✅ Server started!')
console.log(`📋 Peer ID: ${node.peerId.toString()}`)
console.log('📍 Listening addresses:')
node.getMultiaddrs().forEach(addr => {
console.log(` ${addr.toString()}`)
})
// Listen for connections
node.addEventListener('peer:connect', (evt) => {
console.log(`🔗 Peer connected: ${evt.detail.toString()}`)
})
node.addEventListener('peer:disconnect', (evt) => {
console.log(`❌ Peer disconnected: ${evt.detail.toString()}`)
})
console.log('\n🎧 Server ready for ping requests...')
console.log('Press Ctrl+C to exit')
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('\n🛑 Shutting down...')
await node.stop()
process.exit(0)
})
// Keep alive
while (true) {
await new Promise(resolve => setTimeout(resolve, 1000))
}
}
async function runClient(targetAddr, count = 5) {
console.log('🚀 Starting js-libp2p ping client...')
const node = await createNode()
await node.start()
console.log(`📋 Our Peer ID: ${node.peerId.toString()}`)
console.log(`🎯 Target: ${targetAddr}`)
try {
const ma = multiaddr(targetAddr)
const targetPeerId = ma.getPeerId()
if (!targetPeerId) {
throw new Error('Could not extract peer ID from multiaddr')
}
console.log(`🎯 Target Peer ID: ${targetPeerId}`)
console.log('🔗 Connecting to peer...')
const connection = await node.dial(ma)
console.log('✅ Connection established!')
console.log(`🔗 Connected to: ${connection.remotePeer.toString()}`)
// Add a small delay to let the connection fully establish
await new Promise(resolve => setTimeout(resolve, 1000))
const rtts = []
for (let i = 1; i <= count; i++) {
try {
console.log(`\n🏓 Sending ping ${i}/${count}...`);
console.log('[DEBUG] Attempting to open ping stream with protocol: /ipfs/ping/1.0.0');
const start = Date.now()
const stream = await connection.newStream(['/ipfs/ping/1.0.0']).catch(err => {
console.error(`[ERROR] Failed to open ping stream: ${err.message}`);
throw err;
});
console.log('[DEBUG] Ping stream opened successfully');
const latency = await Promise.race([
node.services.ping.ping(connection.remotePeer),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Ping timeout')), 30000) // Increased timeout
)
]).catch(err => {
console.error(`[ERROR] Ping ${i} error: ${err.message}`);
throw err;
});
const rtt = Date.now() - start;
rtts.push(latency)
console.log(`✅ Ping ${i} successful!`)
console.log(` Reported latency: ${latency}ms`)
console.log(` Measured RTT: ${rtt}ms`)
if (i < count) {
await new Promise(resolve => setTimeout(resolve, 1000))
}
} catch (error) {
console.error(`❌ Ping ${i} failed:`, error.message)
// Try to continue with other pings
}
}
// Stats
if (rtts.length > 0) {
const avg = rtts.reduce((a, b) => a + b, 0) / rtts.length
const min = Math.min(...rtts)
const max = Math.max(...rtts)
console.log(`\n📊 Ping Statistics:`)
console.log(` Packets: Sent=${count}, Received=${rtts.length}, Lost=${count - rtts.length}`)
console.log(` Latency: min=${min}ms, avg=${avg.toFixed(2)}ms, max=${max}ms`)
} else {
console.log(`\n📊 All pings failed (${count} attempts)`)
}
} catch (error) {
console.error('❌ Client error:', error.message)
console.error('Stack:', error.stack)
process.exit(1)
} finally {
await node.stop()
console.log('\n⏹ Client stopped')
}
}
async function main() {
const args = process.argv.slice(2)
if (args.length === 0) {
console.log('Usage:')
console.log(' node ping.js server # Start ping server')
console.log(' node ping.js client <multiaddr> [count] # Ping a peer')
console.log('')
console.log('Examples:')
console.log(' node ping.js server')
console.log(' node ping.js client /ip4/127.0.0.1/tcp/12345/p2p/12D3Ko... 5')
process.exit(1)
}
const mode = args[0]
if (mode === 'server') {
await runServer()
} else if (mode === 'client') {
if (args.length < 2) {
console.error('❌ Client mode requires target multiaddr')
process.exit(1)
}
const targetAddr = args[1]
const count = parseInt(args[2]) || 5
await runClient(targetAddr, count)
} else {
console.error('❌ Invalid mode. Use "server" or "client"')
process.exit(1)
}
}
main().catch(console.error)

View File

@ -0,0 +1,241 @@
#!/usr/bin/env node
import { createLibp2p } from 'libp2p'
import { tcp } from '@libp2p/tcp'
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { ping } from '@libp2p/ping'
import { identify } from '@libp2p/identify'
import { multiaddr } from '@multiformats/multiaddr'
import fs from 'fs'
import path from 'path'
// Create logs directory if it doesn't exist
const logsDir = path.join(process.cwd(), '../logs')
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true })
}
// Setup logging
const logFile = path.join(logsDir, 'js_ping_client.log')
const logStream = fs.createWriteStream(logFile, { flags: 'w' })
function log(message) {
const timestamp = new Date().toISOString()
const logLine = `${timestamp} - ${message}\n`
logStream.write(logLine)
console.log(message)
}
async function createNode() {
log('🔧 Creating libp2p node...')
const node = await createLibp2p({
addresses: {
listen: ['/ip4/0.0.0.0/tcp/0'] // Random port
},
transports: [
tcp()
],
connectionEncrypters: [
noise()
],
streamMuxers: [
yamux()
],
services: {
ping: ping({
protocolPrefix: 'ipfs', // Use ipfs prefix to match py-libp2p
maxInboundStreams: 32,
maxOutboundStreams: 64,
timeout: 30000,
runOnTransientConnection: true
}),
identify: identify()
},
connectionManager: {
minConnections: 0,
maxConnections: 100,
dialTimeout: 30000,
maxParallelDials: 10
}
})
log('✅ Node created successfully')
return node
}
async function runClient(targetAddr, count = 5) {
log('🚀 Starting js-libp2p ping client...')
const node = await createNode()
// Add connection event listeners
node.addEventListener('peer:connect', (evt) => {
log(`🔗 Connected to peer: ${evt.detail.toString()}`)
})
node.addEventListener('peer:disconnect', (evt) => {
log(`❌ Disconnected from peer: ${evt.detail.toString()}`)
})
await node.start()
log('✅ Node started')
log(`📋 Our Peer ID: ${node.peerId.toString()}`)
log(`🎯 Target: ${targetAddr}`)
try {
const ma = multiaddr(targetAddr)
const targetPeerId = ma.getPeerId()
if (!targetPeerId) {
throw new Error('Could not extract peer ID from multiaddr')
}
log(`🎯 Target Peer ID: ${targetPeerId}`)
// Parse multiaddr components for debugging
const components = ma.toString().split('/')
log(`📍 Target components: ${components.join(' → ')}`)
log('🔗 Attempting to dial peer...')
const connection = await node.dial(ma)
log('✅ Connection established!')
log(`🔗 Connected to: ${connection.remotePeer.toString()}`)
log(`🔗 Connection status: ${connection.status}`)
log(`🔗 Connection direction: ${connection.direction}`)
// List available protocols
if (connection.remoteAddr) {
log(`🌐 Remote address: ${connection.remoteAddr.toString()}`)
}
// Wait for connection to stabilize
log('⏳ Waiting for connection to stabilize...')
await new Promise(resolve => setTimeout(resolve, 2000))
// Attempt ping sequence
log(`\n🏓 Starting ping sequence (${count} pings)...`)
const rtts = []
for (let i = 1; i <= count; i++) {
try {
log(`\n🏓 Sending ping ${i}/${count}...`)
const start = Date.now()
// Create a more robust ping with better error handling
const pingPromise = node.services.ping.ping(connection.remotePeer)
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Ping timeout (15s)')), 15000)
)
const latency = await Promise.race([pingPromise, timeoutPromise])
const totalRtt = Date.now() - start
rtts.push(latency)
log(`✅ Ping ${i} successful!`)
log(` Reported latency: ${latency}ms`)
log(` Total RTT: ${totalRtt}ms`)
// Wait between pings
if (i < count) {
await new Promise(resolve => setTimeout(resolve, 1000))
}
} catch (error) {
log(`❌ Ping ${i} failed: ${error.message}`)
log(` Error type: ${error.constructor.name}`)
if (error.code) {
log(` Error code: ${error.code}`)
}
// Check if connection is still alive
if (connection.status !== 'open') {
log(`⚠️ Connection status changed to: ${connection.status}`)
break
}
}
}
// Print statistics
if (rtts.length > 0) {
const avg = rtts.reduce((a, b) => a + b, 0) / rtts.length
const min = Math.min(...rtts)
const max = Math.max(...rtts)
const lossRate = ((count - rtts.length) / count * 100).toFixed(1)
log(`\n📊 Ping Statistics:`)
log(` Packets: Sent=${count}, Received=${rtts.length}, Lost=${count - rtts.length}`)
log(` Loss rate: ${lossRate}%`)
log(` Latency: min=${min}ms, avg=${avg.toFixed(2)}ms, max=${max}ms`)
} else {
log(`\n📊 All pings failed (${count} attempts)`)
}
// Close connection gracefully
log('\n🔒 Closing connection...')
await connection.close()
} catch (error) {
log(`❌ Client error: ${error.message}`)
log(` Error type: ${error.constructor.name}`)
if (error.stack) {
log(` Stack trace: ${error.stack}`)
}
process.exit(1)
} finally {
log('🛑 Stopping node...')
await node.stop()
log('⏹️ Client stopped')
logStream.end()
}
}
async function main() {
const args = process.argv.slice(2)
if (args.length === 0) {
console.log('Usage:')
console.log(' node ping-client.js <target-multiaddr> [count]')
console.log('')
console.log('Examples:')
console.log(' node ping-client.js /ip4/127.0.0.1/tcp/8000/p2p/QmExample... 5')
console.log(' node ping-client.js /ip4/127.0.0.1/tcp/8000/p2p/QmExample... 10')
process.exit(1)
}
const targetAddr = args[0]
const count = parseInt(args[1]) || 5
if (count <= 0 || count > 100) {
console.error('❌ Count must be between 1 and 100')
process.exit(1)
}
await runClient(targetAddr, count)
}
// Handle graceful shutdown
process.on('SIGINT', () => {
log('\n👋 Shutting down...')
logStream.end()
process.exit(0)
})
process.on('uncaughtException', (error) => {
log(`💥 Uncaught exception: ${error.message}`)
if (error.stack) {
log(`Stack: ${error.stack}`)
}
logStream.end()
process.exit(1)
})
main().catch((error) => {
log(`💥 Fatal error: ${error.message}`)
if (error.stack) {
log(`Stack: ${error.stack}`)
}
logStream.end()
process.exit(1)
})

View File

@ -0,0 +1,167 @@
#!/usr/bin/env node
import { createLibp2p } from 'libp2p'
import { tcp } from '@libp2p/tcp'
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
import { ping } from '@libp2p/ping'
import { identify } from '@libp2p/identify'
import fs from 'fs'
import path from 'path'
// Create logs directory if it doesn't exist
const logsDir = path.join(process.cwd(), '../logs')
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true })
}
// Setup logging
const logFile = path.join(logsDir, 'js_ping_server.log')
const logStream = fs.createWriteStream(logFile, { flags: 'w' })
function log(message) {
const timestamp = new Date().toISOString()
const logLine = `${timestamp} - ${message}\n`
logStream.write(logLine)
console.log(message)
}
async function createNode(port) {
log('🔧 Creating libp2p node...')
const node = await createLibp2p({
addresses: {
listen: [`/ip4/0.0.0.0/tcp/${port}`]
},
transports: [
tcp()
],
connectionEncrypters: [
noise()
],
streamMuxers: [
yamux()
],
services: {
ping: ping({
protocolPrefix: 'ipfs', // Use ipfs prefix to match py-libp2p
maxInboundStreams: 32,
maxOutboundStreams: 64,
timeout: 30000,
runOnTransientConnection: true
}),
identify: identify()
},
connectionManager: {
minConnections: 0,
maxConnections: 100,
dialTimeout: 30000,
maxParallelDials: 10
}
})
log('✅ Node created successfully')
return node
}
async function runServer(port) {
log('🚀 Starting js-libp2p ping server...')
const node = await createNode(port)
// Add connection event listeners
node.addEventListener('peer:connect', (evt) => {
log(`🔗 New peer connected: ${evt.detail.toString()}`)
})
node.addEventListener('peer:disconnect', (evt) => {
log(`❌ Peer disconnected: ${evt.detail.toString()}`)
})
// Add protocol handler for incoming streams
node.addEventListener('peer:identify', (evt) => {
log(`🔍 Peer identified: ${evt.detail.peerId.toString()}`)
log(` Protocols: ${evt.detail.protocols.join(', ')}`)
log(` Listen addresses: ${evt.detail.listenAddrs.map(addr => addr.toString()).join(', ')}`)
})
await node.start()
log('✅ Node started')
const peerId = node.peerId.toString()
const listenAddrs = node.getMultiaddrs()
log(`📋 Peer ID: ${peerId}`)
log(`🌐 Listen addresses:`)
listenAddrs.forEach(addr => {
log(` ${addr.toString()}`)
})
// Find the main TCP address for easy copy-paste
const tcpAddr = listenAddrs.find(addr =>
addr.toString().includes('/tcp/') &&
!addr.toString().includes('/ws')
)
if (tcpAddr) {
log(`\n🧪 Test with py-libp2p:`)
log(` python ping_client.py ${tcpAddr.toString()}`)
log(`\n🧪 Test with js-libp2p:`)
log(` node ping-client.js ${tcpAddr.toString()}`)
}
log(`\n🏓 Ping service is running with protocol: /ipfs/ping/1.0.0`)
log(`🔐 Security: Noise encryption`)
log(`🚇 Muxer: Yamux stream multiplexing`)
log(`\n⏳ Waiting for connections...`)
log('Press Ctrl+C to exit')
// Keep the server running
return new Promise((resolve, reject) => {
process.on('SIGINT', () => {
log('\n🛑 Shutting down server...')
node.stop().then(() => {
log('⏹️ Server stopped')
logStream.end()
resolve()
}).catch(reject)
})
process.on('uncaughtException', (error) => {
log(`💥 Uncaught exception: ${error.message}`)
if (error.stack) {
log(`Stack: ${error.stack}`)
}
logStream.end()
reject(error)
})
})
}
async function main() {
const args = process.argv.slice(2)
const port = parseInt(args[0]) || 9000
if (port <= 0 || port > 65535) {
console.error('❌ Port must be between 1 and 65535')
process.exit(1)
}
try {
await runServer(port)
} catch (error) {
console.error(`💥 Fatal error: ${error.message}`)
if (error.stack) {
console.error(`Stack: ${error.stack}`)
}
process.exit(1)
}
}
main().catch((error) => {
console.error(`💥 Fatal error: ${error.message}`)
if (error.stack) {
console.error(`Stack: ${error.stack}`)
}
process.exit(1)
})

View File

@ -0,0 +1,398 @@
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"),
],
)
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 js-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()}")
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 js-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:
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")
if rtts:
avg_rtt = sum(rtts) / len(rtts)
min_rtt = min(rtts)
max_rtts = max(rtts)
success_count = len(rtts)
loss_rate = ((count - success_count) / count) * 100
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_rtts:.2f}ms"
)
else:
print(f"\n[STATS] All pings failed ({count} attempts)")
def create_noise_keypair():
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 js-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()
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)
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!")
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 js-libp2p:")
print(f" node ping.js client /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()
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!")
protocols_to_try = [
PING_PROTOCOL_ID,
TProtocol("/ping/1.0.0"),
TProtocol("/libp2p/ping/1.0.0"),
]
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}")
continue
if not stream:
print("[ERROR] Failed to open stream with any ping protocol")
return 1
await send_ping_sequence(stream, count)
await stream.close()
except Exception as e:
print(f"[ERROR] Client error: {e}")
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 js-libp2p.
Uses Noise encryption and Yamux multiplexing for compatibility.
Server mode: Listens for ping requests from js-libp2p or py-libp2p clients.
Client mode: Sends ping requests to js-libp2p or py-libp2p servers.
"""
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
""",
)
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}")
import traceback
traceback.print_exc()
return 1
return 0
if __name__ == "__main__":
exit(main())

View File

@ -0,0 +1,194 @@
#!/usr/bin/env pwsh
# run_test.ps1 - libp2p Interoperability Test Runner (PowerShell)
# Tests py-libp2p <-> js-libp2p ping communication
$ErrorActionPreference = "Stop"
# Colors for output
$Red = "`e[31m"
$Green = "`e[32m"
$Yellow = "`e[33m"
$Blue = "`e[34m"
$Cyan = "`e[36m"
$Reset = "`e[0m"
function Write-ColorOutput {
param([string]$Message, [string]$Color = $Reset)
Write-Host "${Color}${Message}${Reset}"
}
Write-ColorOutput "[CHECK] Checking prerequisites..." $Cyan
if (-not (Get-Command python -ErrorAction SilentlyContinue)) {
Write-ColorOutput "[ERROR] Python not found. Install Python 3.7+" $Red
exit 1
}
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
Write-ColorOutput "[ERROR] Node.js not found. Install Node.js 16+" $Red
exit 1
}
Write-ColorOutput "[CHECK] Checking port 8000..." $Blue
$portCheck = netstat -a -n -o | findstr :8000
if ($portCheck) {
Write-ColorOutput "[ERROR] Port 8000 in use. Free the port." $Red
Write-ColorOutput $portCheck $Yellow
exit 1
}
Write-ColorOutput "[DEBUG] Cleaning up Python processes..." $Blue
Get-Process -Name "python" -ErrorAction SilentlyContinue | Where-Object { $_.CommandLine -like "*ping.py*" } | Stop-Process -Force -ErrorAction SilentlyContinue
Write-ColorOutput "[PYTHON] Starting server on port 8000..." $Yellow
Set-Location -Path "py_node"
$pyLogFile = "py_server_8000.log"
$pyErrLogFile = "py_server_8000.log.err"
$pyDebugLogFile = "ping_debug.log"
if (Test-Path $pyLogFile) { Remove-Item $pyLogFile -Force -ErrorAction SilentlyContinue }
if (Test-Path $pyErrLogFile) { Remove-Item $pyErrLogFile -Force -ErrorAction SilentlyContinue }
if (Test-Path $pyDebugLogFile) { Remove-Item $pyDebugLogFile -Force -ErrorAction SilentlyContinue }
$pyProcess = Start-Process -FilePath "python" -ArgumentList "-u", "ping.py", "server", "--port", "8000" -NoNewWindow -PassThru -RedirectStandardOutput $pyLogFile -RedirectStandardError $pyErrLogFile
Write-ColorOutput "[DEBUG] Python server PID: $($pyProcess.Id)" $Blue
Write-ColorOutput "[DEBUG] Python logs: $((Get-Location).Path)\$pyLogFile, $((Get-Location).Path)\$pyErrLogFile, $((Get-Location).Path)\$pyDebugLogFile" $Blue
$timeoutSeconds = 20
$startTime = Get-Date
$serverStarted = $false
while (((Get-Date) - $startTime).TotalSeconds -lt $timeoutSeconds -and -not $serverStarted) {
if (Test-Path $pyLogFile) {
$content = Get-Content $pyLogFile -Raw -ErrorAction SilentlyContinue
if ($content -match "Server started|Listening") {
$serverStarted = $true
Write-ColorOutput "[OK] Python server started" $Green
}
}
if (Test-Path $pyErrLogFile) {
$errContent = Get-Content $pyErrLogFile -Raw -ErrorAction SilentlyContinue
if ($errContent) {
Write-ColorOutput "[DEBUG] Error log: $errContent" $Yellow
}
}
Start-Sleep -Milliseconds 500
}
if (-not $serverStarted) {
Write-ColorOutput "[ERROR] Python server failed to start" $Red
Write-ColorOutput "[DEBUG] Logs:" $Yellow
if (Test-Path $pyLogFile) { Get-Content $pyLogFile | Write-ColorOutput -Color $Yellow }
if (Test-Path $pyErrLogFile) { Get-Content $pyErrLogFile | Write-ColorOutput -Color $Yellow }
if (Test-Path $pyDebugLogFile) { Get-Content $pyDebugLogFile | Write-ColorOutput -Color $Yellow }
Write-ColorOutput "[DEBUG] Trying foreground run..." $Yellow
python -u ping.py server --port 8000
exit 1
}
# Extract Peer ID
$peerInfo = $null
if (Test-Path $pyLogFile) {
$content = Get-Content $pyLogFile -Raw
$peerIdPattern = "Peer ID:\s*([A-Za-z0-9]+)"
$peerIdMatch = [regex]::Match($content, $peerIdPattern)
if ($peerIdMatch.Success) {
$peerId = $peerIdMatch.Groups[1].Value
$peerInfo = @{
PeerId = $peerId
MultiAddr = "/ip4/127.0.0.1/tcp/8000/p2p/$peerId"
}
Write-ColorOutput "[OK] Peer ID: $peerId" $Cyan
Write-ColorOutput "[OK] MultiAddr: $($peerInfo.MultiAddr)" $Cyan
}
}
if (-not $peerInfo) {
Write-ColorOutput "[ERROR] Could not extract Peer ID" $Red
if (Test-Path $pyLogFile) { Get-Content $pyLogFile | Write-ColorOutput -Color $Yellow }
if (Test-Path $pyErrLogFile) { Get-Content $pyErrLogFile | Write-ColorOutput -Color $Yellow }
if (Test-Path $pyDebugLogFile) { Get-Content $pyDebugLogFile | Write-ColorOutput -Color $Yellow }
Stop-Process -Id $pyProcess.Id -Force -ErrorAction SilentlyContinue
exit 1
}
# Start JavaScript client
Write-ColorOutput "[JAVASCRIPT] Starting client..." $Yellow
Set-Location -Path "../js_node"
$jsLogFile = "test_js_client_to_py_server.log"
$jsErrLogFile = "test_js_client_to_py_server.log.err"
if (Test-Path $jsLogFile) { Remove-Item $jsLogFile -Force -ErrorAction SilentlyContinue }
if (Test-Path $jsErrLogFile) { Remove-Item $jsErrLogFile -Force -ErrorAction SilentlyContinue }
$jsProcess = Start-Process -FilePath "node" -ArgumentList "src/ping.js", "client", $peerInfo.MultiAddr, "3" -NoNewWindow -PassThru -RedirectStandardOutput $jsLogFile -RedirectStandardError $jsErrLogFile
Write-ColorOutput "[DEBUG] JavaScript client PID: $($jsProcess.Id)" $Blue
Write-ColorOutput "[DEBUG] Client logs: $((Get-Location).Path)\$jsLogFile, $((Get-Location).Path)\$jsErrLogFile" $Blue
# Wait for client to complete
$clientTimeout = 10
$clientStart = Get-Date
while (-not $jsProcess.HasExited -and (((Get-Date) - $clientStart).TotalSeconds -lt $clientTimeout)) {
Start-Sleep -Seconds 1
}
if (-not $jsProcess.HasExited) {
Write-ColorOutput "[DEBUG] JavaScript client did not exit, terminating..." $Yellow
Stop-Process -Id $jsProcess.Id -Force -ErrorAction SilentlyContinue
}
Write-ColorOutput "[CHECK] Results..." $Cyan
$success = $false
if (Test-Path $jsLogFile) {
$jsLogContent = Get-Content $jsLogFile -Raw -ErrorAction SilentlyContinue
if ($jsLogContent -match "successful|Ping.*successful") {
$success = $true
Write-ColorOutput "[SUCCESS] Ping test passed" $Green
} else {
Write-ColorOutput "[FAILED] No successful pings" $Red
Write-ColorOutput "[DEBUG] Client log path: $((Get-Location).Path)\$jsLogFile" $Yellow
Write-ColorOutput "Client log:" $Yellow
Write-ColorOutput $jsLogContent $Yellow
if (Test-Path $jsErrLogFile) {
Write-ColorOutput "[DEBUG] Client error log path: $((Get-Location).Path)\$jsErrLogFile" $Yellow
Write-ColorOutput "Client error log:" $Yellow
Get-Content $jsErrLogFile | Write-ColorOutput -Color $Yellow
}
Write-ColorOutput "[DEBUG] Python server log path: $((Get-Location).Path)\..\py_node\$pyLogFile" $Yellow
Write-ColorOutput "Python server log:" $Yellow
if (Test-Path "../py_node/$pyLogFile") {
$pyLogContent = Get-Content "../py_node/$pyLogFile" -Raw -ErrorAction SilentlyContinue
if ($pyLogContent) { Write-ColorOutput $pyLogContent $Yellow } else { Write-ColorOutput "Empty or inaccessible" $Yellow }
} else {
Write-ColorOutput "File not found" $Yellow
}
Write-ColorOutput "[DEBUG] Python server error log path: $((Get-Location).Path)\..\py_node\$pyErrLogFile" $Yellow
Write-ColorOutput "Python server error log:" $Yellow
if (Test-Path "../py_node/$pyErrLogFile") {
$pyErrLogContent = Get-Content "../py_node/$pyErrLogFile" -Raw -ErrorAction SilentlyContinue
if ($pyErrLogContent) { Write-ColorOutput $pyErrLogContent $Yellow } else { Write-ColorOutput "Empty or inaccessible" $Yellow }
} else {
Write-ColorOutput "File not found" $Yellow
}
Write-ColorOutput "[DEBUG] Python debug log path: $((Get-Location).Path)\..\py_node\$pyDebugLogFile" $Yellow
Write-ColorOutput "Python debug log:" $Yellow
if (Test-Path "../py_node/$pyDebugLogFile") {
$pyDebugLogContent = Get-Content "../py_node/$pyDebugLogFile" -Raw -ErrorAction SilentlyContinue
if ($pyDebugLogContent) { Write-ColorOutput $pyDebugLogContent $Yellow } else { Write-ColorOutput "Empty or inaccessible" $Yellow }
} else {
Write-ColorOutput "File not found" $Yellow
}
}
}
Write-ColorOutput "[CLEANUP] Stopping processes..." $Yellow
Stop-Process -Id $pyProcess.Id -Force -ErrorAction SilentlyContinue
Stop-Process -Id $jsProcess.Id -Force -ErrorAction SilentlyContinue
Set-Location -Path "../"
if ($success) {
Write-ColorOutput "[SUCCESS] Test completed" $Green
exit 0
} else {
Write-ColorOutput "[FAILED] Test failed" $Red
exit 1
}

View File

@ -0,0 +1,215 @@
#!/usr/bin/env bash
# run_test.sh - libp2p Interoperability Test Runner (Bash)
# Tests py-libp2p <-> js-libp2p ping communication
set -e
# Colors for output
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'
CYAN='\033[36m'
RESET='\033[0m'
write_color_output() {
local message="$1"
local color="${2:-$RESET}"
echo -e "${color}${message}${RESET}"
}
write_color_output "[CHECK] Checking prerequisites..." "$CYAN"
if ! command -v python3 &> /dev/null && ! command -v python &> /dev/null; then
write_color_output "[ERROR] Python not found. Install Python 3.7+" "$RED"
exit 1
fi
# Use python3 if available, otherwise python
PYTHON_CMD="python3"
if ! command -v python3 &> /dev/null; then
PYTHON_CMD="python"
fi
if ! command -v node &> /dev/null; then
write_color_output "[ERROR] Node.js not found. Install Node.js 16+" "$RED"
exit 1
fi
write_color_output "[CHECK] Checking port 8000..." "$BLUE"
if netstat -tuln 2>/dev/null | grep -q ":8000 " || ss -tuln 2>/dev/null | grep -q ":8000 "; then
write_color_output "[ERROR] Port 8000 in use. Free the port." "$RED"
if command -v netstat &> /dev/null; then
netstat -tuln | grep ":8000 " | write_color_output "$(cat)" "$YELLOW"
elif command -v ss &> /dev/null; then
ss -tuln | grep ":8000 " | write_color_output "$(cat)" "$YELLOW"
fi
exit 1
fi
write_color_output "[DEBUG] Cleaning up Python processes..." "$BLUE"
pkill -f "ping.py" 2>/dev/null || true
write_color_output "[PYTHON] Starting server on port 8000..." "$YELLOW"
cd py_node
PY_LOG_FILE="py_server_8000.log"
PY_ERR_LOG_FILE="py_server_8000.log.err"
PY_DEBUG_LOG_FILE="ping_debug.log"
rm -f "$PY_LOG_FILE" "$PY_ERR_LOG_FILE" "$PY_DEBUG_LOG_FILE"
$PYTHON_CMD -u ping.py server --port 8000 > "$PY_LOG_FILE" 2> "$PY_ERR_LOG_FILE" &
PY_PROCESS_PID=$!
write_color_output "[DEBUG] Python server PID: $PY_PROCESS_PID" "$BLUE"
write_color_output "[DEBUG] Python logs: $(pwd)/$PY_LOG_FILE, $(pwd)/$PY_ERR_LOG_FILE, $(pwd)/$PY_DEBUG_LOG_FILE" "$BLUE"
TIMEOUT_SECONDS=20
START_TIME=$(date +%s)
SERVER_STARTED=false
while [ $(($(date +%s) - START_TIME)) -lt $TIMEOUT_SECONDS ] && [ "$SERVER_STARTED" = false ]; do
if [ -f "$PY_LOG_FILE" ]; then
if grep -q "Server started\|Listening" "$PY_LOG_FILE" 2>/dev/null; then
SERVER_STARTED=true
write_color_output "[OK] Python server started" "$GREEN"
fi
fi
if [ -f "$PY_ERR_LOG_FILE" ] && [ -s "$PY_ERR_LOG_FILE" ]; then
ERR_CONTENT=$(cat "$PY_ERR_LOG_FILE" 2>/dev/null || true)
if [ -n "$ERR_CONTENT" ]; then
write_color_output "[DEBUG] Error log: $ERR_CONTENT" "$YELLOW"
fi
fi
sleep 0.5
done
if [ "$SERVER_STARTED" = false ]; then
write_color_output "[ERROR] Python server failed to start" "$RED"
write_color_output "[DEBUG] Logs:" "$YELLOW"
[ -f "$PY_LOG_FILE" ] && cat "$PY_LOG_FILE" | while read line; do write_color_output "$line" "$YELLOW"; done
[ -f "$PY_ERR_LOG_FILE" ] && cat "$PY_ERR_LOG_FILE" | while read line; do write_color_output "$line" "$YELLOW"; done
[ -f "$PY_DEBUG_LOG_FILE" ] && cat "$PY_DEBUG_LOG_FILE" | while read line; do write_color_output "$line" "$YELLOW"; done
write_color_output "[DEBUG] Trying foreground run..." "$YELLOW"
$PYTHON_CMD -u ping.py server --port 8000
exit 1
fi
# Extract Peer ID
PEER_ID=""
MULTI_ADDR=""
if [ -f "$PY_LOG_FILE" ]; then
CONTENT=$(cat "$PY_LOG_FILE" 2>/dev/null || true)
PEER_ID=$(echo "$CONTENT" | grep -oP "Peer ID:\s*\K[A-Za-z0-9]+" || true)
if [ -n "$PEER_ID" ]; then
MULTI_ADDR="/ip4/127.0.0.1/tcp/8000/p2p/$PEER_ID"
write_color_output "[OK] Peer ID: $PEER_ID" "$CYAN"
write_color_output "[OK] MultiAddr: $MULTI_ADDR" "$CYAN"
fi
fi
if [ -z "$PEER_ID" ]; then
write_color_output "[ERROR] Could not extract Peer ID" "$RED"
[ -f "$PY_LOG_FILE" ] && cat "$PY_LOG_FILE" | while read line; do write_color_output "$line" "$YELLOW"; done
[ -f "$PY_ERR_LOG_FILE" ] && cat "$PY_ERR_LOG_FILE" | while read line; do write_color_output "$line" "$YELLOW"; done
[ -f "$PY_DEBUG_LOG_FILE" ] && cat "$PY_DEBUG_LOG_FILE" | while read line; do write_color_output "$line" "$YELLOW"; done
kill $PY_PROCESS_PID 2>/dev/null || true
exit 1
fi
# Start JavaScript client
write_color_output "[JAVASCRIPT] Starting client..." "$YELLOW"
cd ../js_node
JS_LOG_FILE="test_js_client_to_py_server.log"
JS_ERR_LOG_FILE="test_js_client_to_py_server.log.err"
rm -f "$JS_LOG_FILE" "$JS_ERR_LOG_FILE"
node src/ping.js client "$MULTI_ADDR" 3 > "$JS_LOG_FILE" 2> "$JS_ERR_LOG_FILE" &
JS_PROCESS_PID=$!
write_color_output "[DEBUG] JavaScript client PID: $JS_PROCESS_PID" "$BLUE"
write_color_output "[DEBUG] Client logs: $(pwd)/$JS_LOG_FILE, $(pwd)/$JS_ERR_LOG_FILE" "$BLUE"
# Wait for client to complete
CLIENT_TIMEOUT=10
CLIENT_START=$(date +%s)
while kill -0 $JS_PROCESS_PID 2>/dev/null && [ $(($(date +%s) - CLIENT_START)) -lt $CLIENT_TIMEOUT ]; do
sleep 1
done
if kill -0 $JS_PROCESS_PID 2>/dev/null; then
write_color_output "[DEBUG] JavaScript client did not exit, terminating..." "$YELLOW"
kill $JS_PROCESS_PID 2>/dev/null || true
fi
write_color_output "[CHECK] Results..." "$CYAN"
SUCCESS=false
if [ -f "$JS_LOG_FILE" ]; then
JS_LOG_CONTENT=$(cat "$JS_LOG_FILE" 2>/dev/null || true)
if echo "$JS_LOG_CONTENT" | grep -q "successful\|Ping.*successful"; then
SUCCESS=true
write_color_output "[SUCCESS] Ping test passed" "$GREEN"
else
write_color_output "[FAILED] No successful pings" "$RED"
write_color_output "[DEBUG] Client log path: $(pwd)/$JS_LOG_FILE" "$YELLOW"
write_color_output "Client log:" "$YELLOW"
write_color_output "$JS_LOG_CONTENT" "$YELLOW"
if [ -f "$JS_ERR_LOG_FILE" ]; then
write_color_output "[DEBUG] Client error log path: $(pwd)/$JS_ERR_LOG_FILE" "$YELLOW"
write_color_output "Client error log:" "$YELLOW"
cat "$JS_ERR_LOG_FILE" | while read line; do write_color_output "$line" "$YELLOW"; done
fi
write_color_output "[DEBUG] Python server log path: $(pwd)/../py_node/$PY_LOG_FILE" "$YELLOW"
write_color_output "Python server log:" "$YELLOW"
if [ -f "../py_node/$PY_LOG_FILE" ]; then
PY_LOG_CONTENT=$(cat "../py_node/$PY_LOG_FILE" 2>/dev/null || true)
if [ -n "$PY_LOG_CONTENT" ]; then
write_color_output "$PY_LOG_CONTENT" "$YELLOW"
else
write_color_output "Empty or inaccessible" "$YELLOW"
fi
else
write_color_output "File not found" "$YELLOW"
fi
write_color_output "[DEBUG] Python server error log path: $(pwd)/../py_node/$PY_ERR_LOG_FILE" "$YELLOW"
write_color_output "Python server error log:" "$YELLOW"
if [ -f "../py_node/$PY_ERR_LOG_FILE" ]; then
PY_ERR_LOG_CONTENT=$(cat "../py_node/$PY_ERR_LOG_FILE" 2>/dev/null || true)
if [ -n "$PY_ERR_LOG_CONTENT" ]; then
write_color_output "$PY_ERR_LOG_CONTENT" "$YELLOW"
else
write_color_output "Empty or inaccessible" "$YELLOW"
fi
else
write_color_output "File not found" "$YELLOW"
fi
write_color_output "[DEBUG] Python debug log path: $(pwd)/../py_node/$PY_DEBUG_LOG_FILE" "$YELLOW"
write_color_output "Python debug log:" "$YELLOW"
if [ -f "../py_node/$PY_DEBUG_LOG_FILE" ]; then
PY_DEBUG_LOG_CONTENT=$(cat "../py_node/$PY_DEBUG_LOG_FILE" 2>/dev/null || true)
if [ -n "$PY_DEBUG_LOG_CONTENT" ]; then
write_color_output "$PY_DEBUG_LOG_CONTENT" "$YELLOW"
else
write_color_output "Empty or inaccessible" "$YELLOW"
fi
else
write_color_output "File not found" "$YELLOW"
fi
fi
fi
write_color_output "[CLEANUP] Stopping processes..." "$YELLOW"
kill $PY_PROCESS_PID 2>/dev/null || true
kill $JS_PROCESS_PID 2>/dev/null || true
cd ../
if [ "$SUCCESS" = true ]; then
write_color_output "[SUCCESS] Test completed" "$GREEN"
exit 0
else
write_color_output "[FAILED] Test failed" "$RED"
exit 1
fi

View File

@ -1,5 +0,0 @@
def test_js_libp2p_placeholder():
"""
Placeholder test for js-libp2p interop tests.
"""
assert True, "Placeholder test for js-libp2p interop tests"