Files
aitbc/.windsurf/skills/blockchain-operations/network-diag.py
oib 9b9c5beb23 ```
chore: enhance .gitignore and remove obsolete documentation files

- Reorganize .gitignore with categorized sections for better maintainability
- Add comprehensive ignore patterns for Python, Node.js, databases, logs, and build artifacts
- Add project-specific ignore rules for coordinator, explorer, and deployment files
- Remove outdated documentation: BITCOIN-WALLET-SETUP.md, LOCAL_ASSETS_SUMMARY.md, README-CONTAINER-DEPLOYMENT.md, README-DOMAIN-DEPLOYMENT.md
```
2026-01-24 14:44:51 +01:00

399 lines
14 KiB
Python
Executable File

#!/usr/bin/env python3
"""
AITBC Network Diagnostics Tool
Analyzes network connectivity, peer health, and block propagation
"""
import json
import sys
import time
import socket
import subprocess
from datetime import datetime
from typing import Dict, List, Tuple, Optional
import requests
class NetworkDiagnostics:
def __init__(self, node_url: str = "http://localhost:8545"):
"""Initialize network diagnostics"""
self.node_url = node_url
self.results = {}
def rpc_call(self, method: str, params: List = None) -> Optional[Dict]:
"""Make JSON-RPC call to node"""
try:
response = requests.post(
self.node_url,
json={
"jsonrpc": "2.0",
"method": method,
"params": params or [],
"id": 1
},
timeout=10
)
return response.json().get('result')
except Exception as e:
return None
def check_connectivity(self) -> Dict[str, any]:
"""Check basic network connectivity"""
print("Checking network connectivity...")
results = {
'node_reachable': False,
'dns_resolution': {},
'port_checks': {},
'internet_connectivity': False
}
# Check if node is reachable
try:
response = requests.get(self.node_url, timeout=5)
results['node_reachable'] = response.status_code == 200
except:
pass
# DNS resolution checks
domains = ['aitbc.io', 'api.aitbc.io', 'mainnet.aitbc.io']
for domain in domains:
try:
ip = socket.gethostbyname(domain)
results['dns_resolution'][domain] = {
'resolvable': True,
'ip': ip
}
except:
results['dns_resolution'][domain] = {
'resolvable': False,
'ip': None
}
# Port checks
ports = [
('localhost', 8545, 'RPC'),
('localhost', 8546, 'WS'),
('localhost', 30303, 'P2P TCP'),
('localhost', 30303, 'P2P UDP')
]
for host, port, service in ports:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(3)
result = sock.connect_ex((host, port))
results['port_checks'][f'{host}:{port} ({service})'] = result == 0
sock.close()
# Internet connectivity
try:
response = requests.get('https://google.com', timeout=5)
results['internet_connectivity'] = response.status_code == 200
except:
pass
self.results['connectivity'] = results
return results
def analyze_peers(self) -> Dict[str, any]:
"""Analyze peer connections"""
print("Analyzing peer connections...")
results = {
'peer_count': 0,
'peer_details': [],
'peer_distribution': {},
'connection_types': {},
'latency_stats': {}
}
# Get peer list
peers = self.rpc_call("admin_peers") or []
results['peer_count'] = len(peers)
# Analyze each peer
for peer in peers:
peer_info = {
'id': (peer.get('id', '')[:10] + '...') if peer.get('id') else '',
'address': peer.get('network', {}).get('remoteAddress', 'Unknown'),
'local_address': peer.get('network', {}).get('localAddress', 'Unknown'),
'caps': list(peer.get('protocols', {}).keys()),
'connected_duration': peer.get('network', {}).get('connectedDuration', 0)
}
# Extract IP for geolocation
if ':' in peer_info['address']:
ip = peer_info['address'].split(':')[0]
peer_info['ip'] = ip
# Get country (would use geoip library in production)
try:
# Simple ping test for latency
start = time.time()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((ip, 30303))
latency = (time.time() - start) * 1000 if result == 0 else None
sock.close()
peer_info['latency_ms'] = latency
except:
peer_info['latency_ms'] = None
results['peer_details'].append(peer_info)
# Calculate distribution
countries = {}
for peer in results['peer_details']:
country = peer.get('country', 'Unknown')
countries[country] = countries.get(country, 0) + 1
results['peer_distribution'] = countries
# Calculate latency stats
latencies = [p['latency_ms'] for p in results['peer_details'] if p['latency_ms'] is not None]
if latencies:
results['latency_stats'] = {
'min': min(latencies),
'max': max(latencies),
'avg': sum(latencies) / len(latencies)
}
self.results['peers'] = results
return results
def test_block_propagation(self) -> Dict[str, any]:
"""Test block propagation speed"""
print("Testing block propagation...")
results = {
'latest_block': 0,
'block_age': 0,
'propagation_delay': None,
'uncle_rate': 0,
'network_hashrate': 0
}
# Get latest block
latest_block = self.rpc_call("eth_getBlockByNumber", ["latest", False])
if latest_block:
results['latest_block'] = int(latest_block['number'], 16)
block_timestamp = int(latest_block['timestamp'], 16)
results['block_age'] = int(time.time()) - block_timestamp
# Get uncle rate (check last 100 blocks)
try:
uncle_count = 0
for i in range(100):
block = self.rpc_call("eth_getBlockByNumber", [hex(results['latest_block'] - i), False])
if block and block.get('uncles'):
uncle_count += len(block['uncles'])
results['uncle_rate'] = (uncle_count / 100) * 100
except:
pass
# Get network hashrate
try:
latest = self.rpc_call("eth_getBlockByNumber", ["latest", False])
if latest:
difficulty = int(latest['difficulty'], 16)
block_time = 13 # Average block time for ETH-like chains
results['network_hashrate'] = difficulty / block_time
except:
pass
self.results['block_propagation'] = results
return results
def check_fork_status(self) -> Dict[str, any]:
"""Check for network forks"""
print("Checking for network forks...")
results = {
'current_fork': None,
'fork_blocks': [],
'reorg_detected': False,
'chain_head': {}
}
# Get current fork block
try:
fork_block = self.rpc_call("eth_forkBlock")
if fork_block:
results['current_fork'] = int(fork_block, 16)
except:
pass
# Check for recent reorganizations
try:
# Get last 10 blocks and check for inconsistencies
for i in range(10):
block_num = hex(int(self.rpc_call("eth_blockNumber"), 16) - i)
block = self.rpc_call("eth_getBlockByNumber", [block_num, False])
if block:
results['chain_head'][block_num] = {
'hash': block['hash'],
'parent': block.get('parentHash'),
'number': block['number']
}
except:
pass
self.results['fork_status'] = results
return results
def analyze_network_performance(self) -> Dict[str, any]:
"""Analyze overall network performance"""
print("Analyzing network performance...")
results = {
'rpc_response_time': 0,
'ws_connection': False,
'bandwidth_estimate': 0,
'packet_loss': 0
}
# Test RPC response time
start = time.time()
self.rpc_call("eth_blockNumber")
results['rpc_response_time'] = (time.time() - start) * 1000
# Test WebSocket connection
try:
import websocket
# Would implement actual WS connection test
results['ws_connection'] = True
except:
results['ws_connection'] = False
self.results['performance'] = results
return results
def generate_recommendations(self) -> List[str]:
"""Generate network improvement recommendations"""
recommendations = []
# Connectivity recommendations
if not self.results.get('connectivity', {}).get('node_reachable'):
recommendations.append("Node is not reachable - check if the node is running")
if not self.results.get('connectivity', {}).get('internet_connectivity'):
recommendations.append("No internet connectivity - check network connection")
# Peer recommendations
peer_count = self.results.get('peers', {}).get('peer_count', 0)
if peer_count < 5:
recommendations.append(f"Low peer count ({peer_count}) - check firewall and port forwarding")
# Performance recommendations
rpc_time = self.results.get('performance', {}).get('rpc_response_time', 0)
if rpc_time > 1000:
recommendations.append("High RPC response time - consider optimizing node or upgrading hardware")
# Block propagation recommendations
block_age = self.results.get('block_propagation', {}).get('block_age', 0)
if block_age > 60:
recommendations.append("Stale blocks detected - possible sync issues")
return recommendations
def print_report(self):
"""Print comprehensive diagnostic report"""
print("\n" + "="*60)
print("AITBC Network Diagnostics Report")
print("="*60)
print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Node URL: {self.node_url}")
# Connectivity section
print("\n[Connectivity]")
conn = self.results.get('connectivity', {})
print(f" Node Reachable: {'' if conn.get('node_reachable') else ''}")
print(f" Internet Access: {'' if conn.get('internet_connectivity') else ''}")
for domain, info in conn.get('dns_resolution', {}).items():
status = '' if info['resolvable'] else ''
print(f" DNS {domain}: {status}")
# Peers section
print("\n[Peer Analysis]")
peers = self.results.get('peers', {})
print(f" Connected Peers: {peers.get('peer_count', 0)}")
if peers.get('peer_distribution'):
print(" Geographic Distribution:")
for country, count in list(peers['peer_distribution'].items())[:5]:
print(f" {country}: {count} peers")
if peers.get('latency_stats'):
stats = peers['latency_stats']
print(f" Latency: {stats['avg']:.0f}ms avg (min: {stats['min']:.0f}ms, max: {stats['max']:.0f}ms)")
# Block propagation section
print("\n[Block Propagation]")
prop = self.results.get('block_propagation', {})
print(f" Latest Block: {prop.get('latest_block', 0):,}")
print(f" Block Age: {prop.get('block_age', 0)} seconds")
print(f" Uncle Rate: {prop.get('uncle_rate', 0):.2f}%")
# Performance section
print("\n[Performance]")
perf = self.results.get('performance', {})
print(f" RPC Response Time: {perf.get('rpc_response_time', 0):.0f}ms")
print(f" WebSocket: {'' if perf.get('ws_connection') else ''}")
# Recommendations
recommendations = self.generate_recommendations()
if recommendations:
print("\n[Recommendations]")
for i, rec in enumerate(recommendations, 1):
print(f" {i}. {rec}")
print("\n" + "="*60)
def save_report(self, filename: str):
"""Save detailed report to file"""
report = {
'timestamp': datetime.now().isoformat(),
'node_url': self.node_url,
'results': self.results,
'recommendations': self.generate_recommendations()
}
with open(filename, 'w') as f:
json.dump(report, f, indent=2)
print(f"\nDetailed report saved to: {filename}")
def main():
import argparse
parser = argparse.ArgumentParser(description='AITBC Network Diagnostics')
parser.add_argument('--node', default='http://localhost:8545', help='Node URL')
parser.add_argument('--output', help='Save report to file')
parser.add_argument('--quick', action='store_true', help='Quick diagnostics')
args = parser.parse_args()
# Run diagnostics
diag = NetworkDiagnostics(args.node)
print("Running AITBC network diagnostics...")
print("-" * 40)
# Run all tests
diag.check_connectivity()
if not args.quick:
diag.analyze_peers()
diag.test_block_propagation()
diag.check_fork_status()
diag.analyze_network_performance()
# Print report
diag.print_report()
# Save if requested
if args.output:
diag.save_report(args.output)
if __name__ == "__main__":
main()