mirror of
https://github.com/varun-r-mallya/py-libp2p.git
synced 2025-12-31 20:36:24 +00:00
Add interoperability test for py-libp2p and js-libp2p with enhanced logging
This commit is contained in:
204
tests/interop/js_libp2p/js_node/src/ping.js
Normal file
204
tests/interop/js_libp2p/js_node/src/ping.js
Normal 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)
|
||||
241
tests/interop/js_libp2p/js_node/src/ping_client.js
Normal file
241
tests/interop/js_libp2p/js_node/src/ping_client.js
Normal 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)
|
||||
})
|
||||
167
tests/interop/js_libp2p/js_node/src/ping_server.js
Normal file
167
tests/interop/js_libp2p/js_node/src/ping_server.js
Normal 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)
|
||||
})
|
||||
Reference in New Issue
Block a user