@@ -3,7 +3,8 @@
|
||||
A decentralized peer-to-peer network implementation using WebRTC for communication in a double ring topology with optional Oracle nodes for enhanced network services.
|
||||
|
||||
**🌐 Live Demo**: https://ringnet.cloud
|
||||
**📂 Repository**: https://git.manalejandro.com/ale/ringnet
|
||||
**📂 Repository**: https://git.manalejandro.com/ale/ringnet
|
||||
**📝 Documentation**: https://pad.manalejandro.com/p/2ringsnet
|
||||
|
||||
## 🌟 Features
|
||||
|
||||
|
||||
0
demo-auto-positioning.js
Archivo normal
0
demo-auto-positioning.js
Archivo normal
@@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
console.log(chalk.blue(`
|
||||
📊 RingNet Dashboard Demo
|
||||
|
||||
This demonstration shows how to use the RingNet dashboard
|
||||
to monitor your network in real-time.
|
||||
|
||||
🚀 Step 1: Start the Dashboard Server
|
||||
${chalk.green('npm run start:dashboard')}
|
||||
|
||||
The dashboard will be available at: ${chalk.cyan('http://localhost:3000')}
|
||||
|
||||
🔗 Step 2: Start Nodes with Dashboard Integration
|
||||
${chalk.green('npm run start:oracle -- --port 8080 --dashboard http://localhost:3000')}
|
||||
${chalk.green('npm run start:node -- --port 8081 --bootstrap localhost:8080 --dashboard http://localhost:3000')}
|
||||
${chalk.green('npm run start:node -- --port 8082 --bootstrap localhost:8080 --dashboard http://localhost:3000')}
|
||||
|
||||
📈 Step 3: Monitor Your Network
|
||||
Open ${chalk.cyan('http://localhost:3000')} in your browser to see:
|
||||
|
||||
✅ Real-time Network Statistics
|
||||
- Total active nodes
|
||||
- Oracle nodes count
|
||||
- Total connections
|
||||
- Last update time
|
||||
|
||||
✅ Live Node List
|
||||
- Node status indicators
|
||||
- Node types (Oracle/Regular)
|
||||
- Ring positions
|
||||
- Connection health
|
||||
|
||||
✅ Interactive Ring Topology
|
||||
- Visual ring structure
|
||||
- Node positioning
|
||||
- Click nodes for details
|
||||
- Color-coded by type
|
||||
|
||||
✅ REST API Access
|
||||
- GET /api/nodes - List all nodes
|
||||
- GET /api/network/stats - Network statistics
|
||||
- GET /api/network/topology - Ring topology
|
||||
- GET /api/nodes/:id - Node details
|
||||
|
||||
📡 API Examples:
|
||||
${chalk.cyan('curl http://localhost:3000/api/network/stats')}
|
||||
${chalk.cyan('curl http://localhost:3000/api/nodes')}
|
||||
${chalk.cyan('curl http://localhost:3000/api/network/topology')}
|
||||
|
||||
🔄 Features:
|
||||
- Auto-refresh every 5 seconds
|
||||
- Inactive node detection (5 minute timeout)
|
||||
- Responsive design for mobile/desktop
|
||||
- Real-time topology updates
|
||||
- Connection health monitoring
|
||||
|
||||
The dashboard provides a comprehensive view of your RingNet
|
||||
network without affecting node performance!
|
||||
`));
|
||||
72
demo.js
72
demo.js
@@ -1,72 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import chalk from 'chalk';
|
||||
|
||||
console.log(chalk.blue.bold('🔗 Ring Network Demo Setup\n'));
|
||||
|
||||
console.log(`
|
||||
${chalk.green('Welcome to the Ring Network!')}
|
||||
|
||||
Your two-ring network with WebRTC and Oracle nodes is now ready to use.
|
||||
|
||||
${chalk.yellow('🚀 Quick Start Guide:')}
|
||||
|
||||
1. ${chalk.cyan('Start the first node (Oracle) as bootstrap:')}
|
||||
npm run start:oracle -- --port 8080
|
||||
|
||||
2. ${chalk.cyan('In a new terminal, start a regular node:')}
|
||||
npm run start:node -- --port 8081 --bootstrap localhost:8080
|
||||
|
||||
3. ${chalk.cyan('In another terminal, start another Oracle:')}
|
||||
npm run start:oracle -- --port 8082 --bootstrap localhost:8080
|
||||
|
||||
4. ${chalk.cyan('Add more nodes as needed:')}
|
||||
npm run start:node -- --port 8083 --bootstrap localhost:8080
|
||||
|
||||
${chalk.magenta('💡 Interactive Commands (once nodes are running):')}
|
||||
|
||||
${chalk.yellow('Regular Node Commands:')}
|
||||
• send <message> - Send message through the ring
|
||||
• info - Show network information
|
||||
• peers - List connected peers
|
||||
• help - Show all commands
|
||||
• quit - Exit the node
|
||||
|
||||
${chalk.yellow('Oracle Node Commands (additional):')}
|
||||
• health - Perform network health check
|
||||
• metrics - Show network metrics
|
||||
• analyze - Analyze network topology
|
||||
• store <key> <val> - Store data in distributed storage
|
||||
• get <key> - Retrieve stored data
|
||||
• propose <text> - Create consensus proposal
|
||||
• vote <id> <y/n> - Vote on a proposal
|
||||
|
||||
${chalk.blue('🔮 Oracle Services Available:')}
|
||||
• Network Analysis - Topology and health monitoring
|
||||
• Data Storage - Distributed key-value store
|
||||
• Consensus - Distributed decision making
|
||||
• Routing - Advanced routing strategies
|
||||
• Health Monitoring - Continuous network monitoring
|
||||
• Metrics Collection - Performance and usage statistics
|
||||
|
||||
${chalk.red('📖 Additional Resources:')}
|
||||
• Run example: npm run example
|
||||
• Run tests: npm test
|
||||
• View help: npm start
|
||||
• Read README.md for detailed documentation
|
||||
|
||||
${chalk.green('🎯 Network Features:')}
|
||||
✅ Double ring topology (inner + outer rings)
|
||||
✅ WebRTC peer-to-peer connections
|
||||
✅ Automatic peer discovery
|
||||
✅ Message routing through rings
|
||||
✅ Oracle nodes with enhanced capabilities
|
||||
✅ Distributed data storage
|
||||
✅ Consensus mechanisms
|
||||
✅ Network health monitoring
|
||||
✅ Fault tolerance and resilience
|
||||
|
||||
${chalk.cyan('Happy networking! 🌐')}
|
||||
`);
|
||||
|
||||
process.exit(0);
|
||||
@@ -18,10 +18,7 @@
|
||||
"start:node": "node node.js",
|
||||
"start:dashboard": "cd server && npm start",
|
||||
"dashboard:dev": "cd server && npm run dev",
|
||||
"demo": "node demo.js",
|
||||
"demo:dashboard": "node demo-dashboard.js",
|
||||
"example": "node examples/basic-example.js",
|
||||
"test": "node test/test-network.js"
|
||||
"example": "node examples/basic-example.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@koush/wrtc": "^0.5.3",
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
A REST API and web dashboard for monitoring RingNet network topology, nodes, and connections in real-time.
|
||||
|
||||
**🌐 Live Demo**: https://ringnet.cloud
|
||||
**📂 Repository**: https://git.manalejandro.com/ale/ringnet
|
||||
**📂 Repository**: https://git.manalejandro.com/ale/ringnet
|
||||
**📝 Documentation**: https://pad.manalejandro.com/p/2ringsnet
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.repo-link, .domain-link {
|
||||
.repo-link, .docs-link, .domain-link {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
@@ -52,7 +52,7 @@
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.repo-link:hover, .domain-link:hover {
|
||||
.repo-link:hover, .docs-link:hover, .domain-link:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
@@ -292,6 +292,7 @@
|
||||
<h1>🔗 RingNet Dashboard</h1>
|
||||
<div class="header-links">
|
||||
<a href="https://git.manalejandro.com/ale/ringnet" target="_blank" class="repo-link">📂 Repository</a>
|
||||
<a href="https://pad.manalejandro.com/p/2ringsnet" target="_blank" class="docs-link">📝 Docs</a>
|
||||
<a href="https://ringnet.cloud" target="_blank" class="domain-link">🌐 ringnet.cloud</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -342,6 +343,7 @@
|
||||
<p>
|
||||
RingNet - Decentralized P2P Network |
|
||||
<a href="https://git.manalejandro.com/ale/ringnet" target="_blank">Source Code</a> |
|
||||
<a href="https://pad.manalejandro.com/p/2ringsnet" target="_blank">Documentation</a> |
|
||||
<a href="https://ringnet.cloud" target="_blank">ringnet.cloud</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
0
test/test-auto-positioning.js
Archivo normal
0
test/test-auto-positioning.js
Archivo normal
@@ -1,339 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { RingNode } from '../src/ring-node.js';
|
||||
import { OracleNode } from '../src/oracle-node.js';
|
||||
import chalk from 'chalk';
|
||||
|
||||
console.log(chalk.blue.bold('🧪 Ring Network Test Suite\n'));
|
||||
|
||||
class NetworkTester {
|
||||
constructor() {
|
||||
this.nodes = [];
|
||||
this.testResults = [];
|
||||
}
|
||||
|
||||
async delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
log(message, type = 'info') {
|
||||
const colors = {
|
||||
info: chalk.blue,
|
||||
success: chalk.green,
|
||||
error: chalk.red,
|
||||
warning: chalk.yellow
|
||||
};
|
||||
console.log(`${colors[type]}${message}`);
|
||||
}
|
||||
|
||||
async runTest(name, testFn) {
|
||||
this.log(`\n🔬 Running test: ${name}`, 'info');
|
||||
try {
|
||||
const startTime = Date.now();
|
||||
await testFn();
|
||||
const duration = Date.now() - startTime;
|
||||
this.log(`✅ Test passed: ${name} (${duration}ms)`, 'success');
|
||||
this.testResults.push({ name, status: 'passed', duration });
|
||||
} catch (error) {
|
||||
this.log(`❌ Test failed: ${name} - ${error.message}`, 'error');
|
||||
this.testResults.push({ name, status: 'failed', error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
async setupBasicNetwork() {
|
||||
this.log('🏗️ Setting up basic network with 1 Oracle and 2 regular nodes', 'info');
|
||||
|
||||
// Create Oracle node
|
||||
const oracle = new OracleNode({
|
||||
id: 'oracle-1',
|
||||
port: 8080
|
||||
});
|
||||
this.nodes.push(oracle);
|
||||
|
||||
// Create regular nodes
|
||||
const node1 = new RingNode({
|
||||
id: 'node-1',
|
||||
port: 8081
|
||||
});
|
||||
this.nodes.push(node1);
|
||||
|
||||
const node2 = new RingNode({
|
||||
id: 'node-2',
|
||||
port: 8082
|
||||
});
|
||||
this.nodes.push(node2);
|
||||
|
||||
// Allow nodes to initialize
|
||||
await this.delay(2000);
|
||||
|
||||
return { oracle, node1, node2 };
|
||||
}
|
||||
|
||||
async testNodeInitialization() {
|
||||
const { oracle, node1, node2 } = await this.setupBasicNetwork();
|
||||
|
||||
// Verify nodes are properly initialized
|
||||
if (!oracle.id || !oracle.isOracle) {
|
||||
throw new Error('Oracle node not properly initialized');
|
||||
}
|
||||
|
||||
if (!node1.id || node1.isOracle) {
|
||||
throw new Error('Regular node 1 not properly initialized');
|
||||
}
|
||||
|
||||
if (!node2.id || node2.isOracle) {
|
||||
throw new Error('Regular node 2 not properly initialized');
|
||||
}
|
||||
|
||||
this.log(`Oracle ID: ${oracle.id}`, 'info');
|
||||
this.log(`Node 1 ID: ${node1.id}`, 'info');
|
||||
this.log(`Node 2 ID: ${node2.id}`, 'info');
|
||||
}
|
||||
|
||||
async testPeerConnections() {
|
||||
const { oracle, node1, node2 } = this.nodes.length > 0 ?
|
||||
{ oracle: this.nodes[0], node1: this.nodes[1], node2: this.nodes[2] } :
|
||||
await this.setupBasicNetwork();
|
||||
|
||||
// Simulate peer connections
|
||||
// In a real test, we would actually establish WebRTC connections
|
||||
// For this test, we'll simulate the connection state
|
||||
|
||||
oracle.knownNodes.set(node1.id, { port: node1.port });
|
||||
oracle.knownNodes.set(node2.id, { port: node2.port });
|
||||
|
||||
node1.knownNodes.set(oracle.id, { port: oracle.port, isOracle: true });
|
||||
node1.knownNodes.set(node2.id, { port: node2.port });
|
||||
|
||||
node2.knownNodes.set(oracle.id, { port: oracle.port, isOracle: true });
|
||||
node2.knownNodes.set(node1.id, { port: node1.port });
|
||||
|
||||
// Update oracle nodes list
|
||||
node1.oracleNodes.add(oracle.id);
|
||||
node2.oracleNodes.add(oracle.id);
|
||||
|
||||
this.log(`Oracle knows ${oracle.knownNodes.size} nodes`, 'info');
|
||||
this.log(`Node 1 knows ${node1.knownNodes.size} nodes`, 'info');
|
||||
this.log(`Node 2 knows ${node2.knownNodes.size} nodes`, 'info');
|
||||
}
|
||||
|
||||
async testRingTopology() {
|
||||
const { oracle, node1, node2 } = this.nodes.length > 0 ?
|
||||
{ oracle: this.nodes[0], node1: this.nodes[1], node2: this.nodes[2] } :
|
||||
await this.setupBasicNetwork();
|
||||
|
||||
// Simulate ring topology setup
|
||||
// Oracle -> Node1 -> Node2 -> Oracle (inner ring)
|
||||
oracle.rings.inner.right = node1.id;
|
||||
oracle.rings.inner.left = node2.id;
|
||||
|
||||
node1.rings.inner.left = oracle.id;
|
||||
node1.rings.inner.right = node2.id;
|
||||
|
||||
node2.rings.inner.left = node1.id;
|
||||
node2.rings.inner.right = oracle.id;
|
||||
|
||||
// Oracle -> Node2 -> Node1 -> Oracle (outer ring - reversed)
|
||||
oracle.rings.outer.right = node2.id;
|
||||
oracle.rings.outer.left = node1.id;
|
||||
|
||||
node1.rings.outer.left = node2.id;
|
||||
node1.rings.outer.right = oracle.id;
|
||||
|
||||
node2.rings.outer.left = oracle.id;
|
||||
node2.rings.outer.right = node1.id;
|
||||
|
||||
// Verify ring integrity
|
||||
const oracleHasValidRings = oracle.rings.inner.left && oracle.rings.inner.right &&
|
||||
oracle.rings.outer.left && oracle.rings.outer.right;
|
||||
|
||||
if (!oracleHasValidRings) {
|
||||
throw new Error('Oracle rings not properly configured');
|
||||
}
|
||||
|
||||
this.log('Ring topology established successfully', 'success');
|
||||
}
|
||||
|
||||
async testOracleServices() {
|
||||
const oracle = this.nodes[0];
|
||||
|
||||
if (!oracle.isOracle) {
|
||||
throw new Error('First node is not an Oracle');
|
||||
}
|
||||
|
||||
// Test network analysis
|
||||
const analysis = oracle.analyzeNetwork();
|
||||
if (!analysis.networkSize || !analysis.ringTopology) {
|
||||
throw new Error('Network analysis failed');
|
||||
}
|
||||
this.log(`Network analysis: ${analysis.networkSize} nodes`, 'info');
|
||||
|
||||
// Test data storage
|
||||
const storeResult = oracle.handleDataStorage({
|
||||
operation: 'set',
|
||||
key: 'test-key',
|
||||
value: 'test-value'
|
||||
});
|
||||
|
||||
if (!storeResult.success) {
|
||||
throw new Error('Data storage (set) failed');
|
||||
}
|
||||
|
||||
const getResult = oracle.handleDataStorage({
|
||||
operation: 'get',
|
||||
key: 'test-key'
|
||||
});
|
||||
|
||||
if (!getResult.success || getResult.value !== 'test-value') {
|
||||
throw new Error('Data storage (get) failed');
|
||||
}
|
||||
|
||||
this.log('Oracle data storage working correctly', 'success');
|
||||
|
||||
// Test consensus
|
||||
const proposalId = 'test-proposal-' + Date.now();
|
||||
const consensusResult = oracle.handleConsensus({
|
||||
proposalId,
|
||||
proposal: 'Test proposal for network upgrade'
|
||||
});
|
||||
|
||||
if (!consensusResult.success) {
|
||||
throw new Error('Consensus proposal creation failed');
|
||||
}
|
||||
|
||||
this.log('Oracle consensus mechanism working', 'success');
|
||||
|
||||
// Test health check
|
||||
const health = oracle.performHealthCheck();
|
||||
if (!health.checks) {
|
||||
throw new Error('Health check failed');
|
||||
}
|
||||
|
||||
this.log(`Health check: ${health.healthy ? 'Healthy' : 'Issues detected'}`,
|
||||
health.healthy ? 'success' : 'warning');
|
||||
}
|
||||
|
||||
async testMessageRouting() {
|
||||
// This test simulates message routing through the ring
|
||||
// In a real implementation, this would test actual WebRTC communication
|
||||
|
||||
const oracle = this.nodes[0];
|
||||
let messageReceived = false;
|
||||
|
||||
// Set up message handler
|
||||
oracle.on('ringMessage', ({ from, payload }) => {
|
||||
if (payload.type === 'test-message') {
|
||||
messageReceived = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Simulate sending a message
|
||||
oracle.emit('ringMessage', {
|
||||
from: 'test-sender',
|
||||
payload: { type: 'test-message', content: 'Hello Ring!' }
|
||||
});
|
||||
|
||||
await this.delay(100);
|
||||
|
||||
if (!messageReceived) {
|
||||
throw new Error('Message routing failed');
|
||||
}
|
||||
|
||||
this.log('Message routing test passed', 'success');
|
||||
}
|
||||
|
||||
async testNetworkResilience() {
|
||||
const { oracle, node1, node2 } = this.nodes.length > 0 ?
|
||||
{ oracle: this.nodes[0], node1: this.nodes[1], node2: this.nodes[2] } :
|
||||
await this.setupBasicNetwork();
|
||||
|
||||
// Simulate node disconnection
|
||||
const originalPeerCount = oracle.knownNodes.size;
|
||||
|
||||
// Remove node2 from network
|
||||
oracle.handlePeerDisconnection(node2.id);
|
||||
node1.handlePeerDisconnection(node2.id);
|
||||
|
||||
if (oracle.knownNodes.has(node2.id)) {
|
||||
throw new Error('Node not properly removed from network');
|
||||
}
|
||||
|
||||
// Verify network adapted to disconnection
|
||||
const newPeerCount = oracle.knownNodes.size;
|
||||
if (newPeerCount >= originalPeerCount) {
|
||||
throw new Error('Network did not adapt to peer disconnection');
|
||||
}
|
||||
|
||||
this.log(`Network adapted to disconnection: ${originalPeerCount} -> ${newPeerCount} nodes`, 'success');
|
||||
}
|
||||
|
||||
async cleanup() {
|
||||
this.log('\n🧹 Cleaning up test environment', 'info');
|
||||
|
||||
for (const node of this.nodes) {
|
||||
try {
|
||||
await node.destroy();
|
||||
} catch (error) {
|
||||
this.log(`Warning: Error cleaning up node ${node.id}: ${error.message}`, 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
this.nodes = [];
|
||||
}
|
||||
|
||||
printResults() {
|
||||
this.log('\n📊 Test Results Summary:', 'info');
|
||||
|
||||
const passed = this.testResults.filter(r => r.status === 'passed').length;
|
||||
const failed = this.testResults.filter(r => r.status === 'failed').length;
|
||||
|
||||
this.log(`✅ Passed: ${passed}`, 'success');
|
||||
this.log(`❌ Failed: ${failed}`, failed > 0 ? 'error' : 'info');
|
||||
|
||||
if (failed > 0) {
|
||||
this.log('\nFailed tests:', 'error');
|
||||
this.testResults
|
||||
.filter(r => r.status === 'failed')
|
||||
.forEach(r => {
|
||||
this.log(` - ${r.name}: ${r.error}`, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
const totalDuration = this.testResults.reduce((sum, r) => sum + (r.duration || 0), 0);
|
||||
this.log(`\nTotal test duration: ${totalDuration}ms`, 'info');
|
||||
|
||||
return failed === 0;
|
||||
}
|
||||
|
||||
async runAllTests() {
|
||||
try {
|
||||
await this.runTest('Node Initialization', () => this.testNodeInitialization());
|
||||
await this.runTest('Peer Connections', () => this.testPeerConnections());
|
||||
await this.runTest('Ring Topology', () => this.testRingTopology());
|
||||
await this.runTest('Oracle Services', () => this.testOracleServices());
|
||||
await this.runTest('Message Routing', () => this.testMessageRouting());
|
||||
await this.runTest('Network Resilience', () => this.testNetworkResilience());
|
||||
} finally {
|
||||
await this.cleanup();
|
||||
}
|
||||
|
||||
return this.printResults();
|
||||
}
|
||||
}
|
||||
|
||||
// Run tests if this file is executed directly
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const tester = new NetworkTester();
|
||||
|
||||
tester.runAllTests()
|
||||
.then(success => {
|
||||
console.log(chalk.blue.bold('\n🏁 Test suite completed'));
|
||||
process.exit(success ? 0 : 1);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(chalk.red.bold(`\n💥 Test suite crashed: ${error.message}`));
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export { NetworkTester };
|
||||
Referencia en una nueva incidencia
Block a user