initial commit

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-06-14 00:46:35 +02:00
commit 7765367300
Se han modificado 12 ficheros con 2785 adiciones y 0 borrados

3
.gitignore vendido Archivo normal
Ver fichero

@@ -0,0 +1,3 @@
node_modules
*-lock.json
*.lock

274
README.md Archivo normal
Ver fichero

@@ -0,0 +1,274 @@
# Ring Network - Two Ring Topology with WebRTC
A decentralized peer-to-peer network implementation using WebRTC for communication in a double ring topology with optional Oracle nodes for enhanced network services.
## 🌟 Features
### Core Network Features
- **Double Ring Topology**: Inner and outer rings for redundancy and efficient routing
- **WebRTC Peer-to-Peer**: Direct browser-to-browser/node-to-node communication
- **Automatic Discovery**: Nodes can discover and connect to each other
- **Message Routing**: Efficient message propagation through ring structures
- **Network Resilience**: Automatic adaptation to node connections/disconnections
### Oracle Node Capabilities
- **Network Analysis**: Topology analysis and health monitoring
- **Distributed Data Storage**: Replicated data storage across Oracle nodes
- **Consensus Mechanisms**: Distributed decision making
- **Advanced Routing**: Sophisticated routing strategies
- **Health Monitoring**: Continuous network health assessment
- **Metrics Collection**: Network performance and usage statistics
## 🏗️ Architecture
```
Inner Ring: A → B → C → A
Outer Ring: A ← B ← C ← A
Where:
- A, B, C are network nodes
- → indicates message flow direction
- Each node maintains connections to its neighbors in both rings
```
### Node Types
1. **Regular Nodes**: Basic network participants that route messages
2. **Oracle Nodes**: Enhanced nodes providing additional services
## 🚀 Quick Start
### Installation
```bash
# Clone or create the project directory
cd ringnet
# Install dependencies
npm install
```
### Running the Network
1. **Start the first node (Oracle) as bootstrap:**
```bash
npm run start:oracle -- --port 8080
```
2. **Start additional nodes:**
```bash
# Regular node
npm run start:node -- --port 8081 --bootstrap localhost:8080
# Another Oracle node
npm run start:oracle -- --port 8082 --bootstrap localhost:8080
```
3. **Start more nodes:**
```bash
npm run start:node -- --port 8083 --bootstrap localhost:8080
```
## 📖 Usage
### Command Line Options
```bash
--port <port> # Port to listen on (default: random)
--id <id> # Node ID (default: auto-generated UUID)
--bootstrap <addr> # Bootstrap node address (host:port)
--position <pos> # Initial ring position (default: 0)
--help # Show help message
```
### Interactive Commands
Once a node is running, you can use these commands:
#### Regular Node Commands
- `send <message>` - Send a message through the ring
- `info` - Show network information
- `peers` - List connected peers
- `help` - Show available commands
- `quit` - Exit the node
#### Oracle Node Commands (additional)
- `health` - Perform network health check
- `metrics` - Show network metrics
- `analyze` - Analyze network topology
- `store <key> <value>` - Store data in distributed storage
- `get <key>` - Retrieve data from storage
- `propose <text>` - Create a consensus proposal
- `vote <id> <yes|no>` - Vote on a proposal
## 🔮 Oracle Services
Oracle nodes provide enhanced services to the network:
### 1. Network Analysis (`network-analysis`)
- Topology mapping and analysis
- Network health assessment
- Performance recommendations
### 2. Data Storage (`data-storage`)
- Distributed key-value storage
- Automatic replication across Oracles
- TTL (Time-To-Live) support
### 3. Consensus (`consensus`)
- Proposal creation and voting
- Majority-based decision making
- Distributed agreement protocols
### 4. Routing (`routing`)
- Advanced routing strategies
- Shortest path calculation
- Multi-path routing options
### 5. Health Monitoring (`health-check`)
- Continuous network monitoring
- Failure detection and reporting
- Recovery recommendations
### 6. Network Metrics (`network-metrics`)
- Performance statistics
- Usage analytics
- Historical data tracking
## 🧪 Testing
Run the test suite to verify network functionality:
```bash
npm test
```
The test suite covers:
- Node initialization
- Peer connections
- Ring topology formation
- Oracle services
- Message routing
- Network resilience
## 📝 API Reference
### RingNode Class
```javascript
import { RingNode } from './src/ring-node.js';
const node = new RingNode({
id: 'optional-id',
port: 8080,
ringPosition: 0,
isOracle: false
});
// Events
node.on('peerConnected', (peerId) => { });
node.on('peerDisconnected', (peerId) => { });
node.on('ringMessage', ({ from, payload }) => { });
node.on('networkUpdate', ({ from, payload }) => { });
// Methods
node.sendRingMessage(payload, ring);
node.joinRing(bootstrapNode);
node.getNetworkInfo();
node.destroy();
```
### OracleNode Class
```javascript
import { OracleNode } from './src/oracle-node.js';
const oracle = new OracleNode({
id: 'oracle-1',
port: 8080
});
// Oracle-specific methods
oracle.analyzeNetwork();
oracle.handleDataStorage({ operation: 'set', key: 'test', value: 'data' });
oracle.handleConsensus({ proposalId: 'prop1', proposal: 'Upgrade network' });
oracle.performHealthCheck();
oracle.getNetworkMetrics();
```
## 🔧 Configuration
### WebRTC Configuration
The nodes use public STUN servers by default:
- `stun:stun.l.google.com:19302`
- `stun:stun1.l.google.com:19302`
For production use, consider setting up your own TURN servers.
### Network Parameters
- **Message History Size**: 1000 messages (prevents loops)
- **Data TTL**: 1 hour default (configurable)
- **Health Check Interval**: 30 seconds
- **Metrics Collection**: 10 seconds
- **Cleanup Interval**: 5 minutes
## 🛡️ Security Considerations
- **Message Integrity**: All messages should be signed in production
- **Node Authentication**: Implement proper node authentication
- **Data Encryption**: Encrypt sensitive data in storage
- **Rate Limiting**: Implement rate limiting for message propagation
- **Access Control**: Restrict Oracle services to authorized nodes
## 🐛 Troubleshooting
### Common Issues
1. **Connection Failures**
- Check firewall settings
- Verify port availability
- Ensure WebRTC support
2. **Bootstrap Node Unreachable**
- Verify bootstrap node is running
- Check network connectivity
- Confirm port and address
3. **Ring Formation Issues**
- Allow time for topology stabilization
- Check node discovery process
- Verify peer connections
### Debug Mode
Enable debug logging by setting environment variable:
```bash
DEBUG=ringnet:* npm start
```
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch
3. Implement your changes
4. Add tests for new functionality
5. Run the test suite
6. Submit a pull request
## 📄 License
MIT License - see LICENSE file for details.
## 🔗 Related Projects
- [WebRTC](https://webrtc.org/) - Real-time communication
- [Node.js WebRTC](https://github.com/node-webrtc/node-webrtc) - WebRTC for Node.js
- [Chord DHT](https://en.wikipedia.org/wiki/Chord_(peer-to-peer)) - Distributed hash table
- [Kademlia](https://en.wikipedia.org/wiki/Kademlia) - Distributed hash table protocol
## 📚 Further Reading
- [Peer-to-Peer Networks](https://en.wikipedia.org/wiki/Peer-to-peer)
- [Distributed Systems](https://en.wikipedia.org/wiki/Distributed_computing)
- [WebRTC Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API)
- [Consensus Algorithms](https://en.wikipedia.org/wiki/Consensus_(computer_science))

72
demo.js Archivo normal
Ver fichero

@@ -0,0 +1,72 @@
#!/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);

321
examples/basic-example.js Archivo normal
Ver fichero

@@ -0,0 +1,321 @@
#!/usr/bin/env node
import { RingNode } from '../src/ring-node.js';
import { OracleNode } from '../src/oracle-node.js';
import chalk from 'chalk';
/**
* Example script demonstrating Ring Network usage
* This creates a simple network and shows basic operations
*/
console.log(chalk.blue.bold('🔗 Ring Network Example\n'));
class NetworkExample {
constructor() {
this.nodes = [];
this.isRunning = false;
}
async delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async createNetwork() {
console.log(chalk.cyan('🏗️ Creating example network...'));
// Create Oracle node (bootstrap)
const oracle = new OracleNode({
id: 'oracle-example',
port: 9000
});
this.nodes.push(oracle);
// Create regular nodes
const node1 = new RingNode({
id: 'node-1-example',
port: 9001
});
this.nodes.push(node1);
const node2 = new RingNode({
id: 'node-2-example',
port: 9002
});
this.nodes.push(node2);
console.log(chalk.green('✅ Created 3 nodes: 1 Oracle + 2 Regular'));
// Set up event handlers
this.setupEventHandlers();
// Allow nodes to initialize
await this.delay(2000);
return { oracle, node1, node2 };
}
setupEventHandlers() {
this.nodes.forEach((node, index) => {
const nodeType = node.isOracle ? 'Oracle' : 'Node';
const nodeColor = node.isOracle ? chalk.yellow : chalk.blue;
node.on('peerConnected', (peerId) => {
console.log(nodeColor(`${nodeType} ${index}: Connected to ${peerId.substring(0, 8)}`));
});
node.on('peerDisconnected', (peerId) => {
console.log(nodeColor(`${nodeType} ${index}: Disconnected from ${peerId.substring(0, 8)}`));
});
node.on('ringMessage', ({ from, payload }) => {
console.log(nodeColor(`${nodeType} ${index}: Received message from ${from.substring(0, 8)}: ${JSON.stringify(payload)}`));
});
});
}
async demonstrateBasicMessaging() {
console.log(chalk.magenta('\n📨 Demonstrating basic messaging...'));
const [oracle, node1, node2] = this.nodes;
// Simulate ring connections for demonstration
// In real usage, these would be established via WebRTC
oracle.rings.inner.right = node1.id;
node1.rings.inner.right = node2.id;
node2.rings.inner.right = oracle.id;
// Simulate message sending
oracle.emit('ringMessage', {
from: oracle.id,
payload: { type: 'greeting', message: 'Hello from Oracle!' }
});
node1.emit('ringMessage', {
from: node1.id,
payload: { type: 'response', message: 'Node 1 received the message!' }
});
await this.delay(1000);
console.log(chalk.green('✅ Basic messaging demonstration complete'));
}
async demonstrateOracleServices() {
console.log(chalk.yellow('\n🔮 Demonstrating Oracle services...'));
const oracle = this.nodes[0];
// Network Analysis
console.log(chalk.cyan('\n1. Network Analysis:'));
const analysis = oracle.analyzeNetwork();
console.log(` - Network size: ${analysis.networkSize}`);
console.log(` - Network health: ${analysis.networkHealth.overall}%`);
console.log(` - Recommendations: ${analysis.recommendations.length}`);
// Data Storage
console.log(chalk.cyan('\n2. Data Storage:'));
const storeResult = oracle.handleDataStorage({
operation: 'set',
key: 'example-key',
value: { message: 'Hello World!', timestamp: Date.now() }
});
console.log(` - Data stored: ${storeResult.success}`);
const retrieveResult = oracle.handleDataStorage({
operation: 'get',
key: 'example-key'
});
console.log(` - Data retrieved: ${retrieveResult.success}`);
console.log(` - Value: ${JSON.stringify(retrieveResult.value)}`);
// Consensus
console.log(chalk.cyan('\n3. Consensus:'));
const proposalId = 'example-proposal-' + Date.now();
const consensusResult = oracle.handleConsensus({
proposalId,
proposal: 'Upgrade network protocol to v2.0'
});
console.log(` - Proposal created: ${consensusResult.success}`);
console.log(` - Proposal ID: ${proposalId}`);
// Health Check
console.log(chalk.cyan('\n4. Health Check:'));
const health = oracle.performHealthCheck();
console.log(` - Network healthy: ${health.healthy}`);
console.log(` - Connected peers: ${health.checks.webrtc}`);
console.log(` - Uptime: ${Math.round(health.checks.uptime / 1000)}s`);
// Metrics
console.log(chalk.cyan('\n5. Network Metrics:'));
const metrics = oracle.getNetworkMetrics();
console.log(` - Messages processed: ${metrics.messagesProcessed}`);
console.log(` - Queries answered: ${metrics.queriesAnswered}`);
console.log(` - Data store size: ${metrics.dataStoreSize}`);
console.log(chalk.green('✅ Oracle services demonstration complete'));
}
async demonstrateNetworkTopology() {
console.log(chalk.blue('\n🔗 Demonstrating network topology...'));
const [oracle, node1, node2] = this.nodes;
// Show network information for each node
this.nodes.forEach((node, index) => {
const info = node.getNetworkInfo();
const nodeType = node.isOracle ? 'Oracle' : 'Node';
console.log(chalk.cyan(`\n${nodeType} ${index} (${info.nodeId.substring(0, 8)}):`));
console.log(` - Port: ${info.port}`);
console.log(` - Known nodes: ${info.knownNodes.length}`);
console.log(` - Oracle nodes: ${info.oracleNodes.length}`);
console.log(` - Inner ring: L=${info.rings.inner.left?.substring(0, 8) || 'none'} R=${info.rings.inner.right?.substring(0, 8) || 'none'}`);
console.log(` - Outer ring: L=${info.rings.outer.left?.substring(0, 8) || 'none'} R=${info.rings.outer.right?.substring(0, 8) || 'none'}`);
});
console.log(chalk.green('\n✅ Network topology demonstration complete'));
}
async demonstrateAdvancedFeatures() {
console.log(chalk.magenta('\n⚡ Demonstrating advanced features...'));
const oracle = this.nodes[0];
// Routing strategies
console.log(chalk.cyan('\n1. Routing Strategies:'));
const routingResult = oracle.handleRouting({
destination: 'target-node',
message: { type: 'test', data: 'routing test' },
strategy: 'ring-flood'
});
console.log(` - Ring flood routing: ${routingResult.success}`);
// Advanced consensus
console.log(chalk.cyan('\n2. Advanced Consensus:'));
const advancedProposal = 'proposal-' + Date.now();
oracle.handleConsensus({
proposalId: advancedProposal,
proposal: 'Implement new security protocol'
});
// Simulate vote
oracle.handleConsensus({
proposalId: advancedProposal,
vote: 'yes'
});
console.log(` - Proposal "${advancedProposal}" created and voted on`);
// Network monitoring
console.log(chalk.cyan('\n3. Network Monitoring:'));
oracle.monitorNetworkHealth();
const recentEvents = oracle.networkMetrics.networkEvents.slice(-3);
console.log(` - Recent network events: ${recentEvents.length}`);
recentEvents.forEach(event => {
console.log(` * ${event.type} at ${new Date(event.timestamp).toLocaleTimeString()}`);
});
console.log(chalk.green('✅ Advanced features demonstration complete'));
}
async simulateNetworkActivity() {
console.log(chalk.blue('\n🎭 Simulating network activity...'));
const oracle = this.nodes[0];
// Simulate periodic activity
for (let i = 0; i < 5; i++) {
// Random data storage
oracle.handleDataStorage({
operation: 'set',
key: `activity-${i}`,
value: { activity: `Simulated activity ${i}`, timestamp: Date.now() }
});
// Simulate message processing
oracle.networkMetrics.messagesProcessed++;
oracle.networkMetrics.queriesAnswered++;
await this.delay(500);
if (i % 2 === 0) {
console.log(chalk.blue(` - Activity ${i + 1}/5: Data stored and metrics updated`));
}
}
console.log(chalk.green('✅ Network activity simulation complete'));
}
async cleanup() {
console.log(chalk.red('\n🧹 Cleaning up example network...'));
for (const node of this.nodes) {
try {
await node.destroy();
} catch (error) {
console.log(chalk.yellow(`Warning: ${error.message}`));
}
}
this.nodes = [];
console.log(chalk.green('✅ Cleanup complete'));
}
async runExample() {
try {
this.isRunning = true;
// Create network
await this.createNetwork();
// Run demonstrations
await this.demonstrateBasicMessaging();
await this.demonstrateOracleServices();
await this.demonstrateNetworkTopology();
await this.demonstrateAdvancedFeatures();
await this.simulateNetworkActivity();
console.log(chalk.green.bold('\n🎉 Example completed successfully!'));
console.log(chalk.blue('\nThis example demonstrated:'));
console.log(' ✅ Network creation and initialization');
console.log(' ✅ Basic messaging between nodes');
console.log(' ✅ Oracle services (analysis, storage, consensus, health)');
console.log(' ✅ Network topology visualization');
console.log(' ✅ Advanced features (routing, monitoring)');
console.log(' ✅ Simulated network activity');
console.log(chalk.cyan('\nTo start a real network:'));
console.log(' npm run start:oracle -- --port 8080');
console.log(' npm run start:node -- --port 8081 --bootstrap localhost:8080');
} catch (error) {
console.error(chalk.red.bold(`\n💥 Example failed: ${error.message}`));
console.error(error.stack);
} finally {
await this.cleanup();
this.isRunning = false;
}
}
}
// Handle graceful shutdown
process.on('SIGINT', async () => {
console.log(chalk.yellow('\n🛑 Shutting down example...'));
process.exit(0);
});
// Run example if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
const example = new NetworkExample();
example.runExample()
.then(() => {
console.log(chalk.blue('\n👋 Example finished. Goodbye!'));
process.exit(0);
})
.catch(error => {
console.error(chalk.red.bold(`\n💥 Example crashed: ${error.message}`));
process.exit(1);
});
}
export { NetworkExample };

74
index.js Archivo normal
Ver fichero

@@ -0,0 +1,74 @@
#!/usr/bin/env node
import chalk from 'chalk';
const args = process.argv.slice(2);
if (args.length === 0 || args.includes('--help')) {
console.log(`
${chalk.blue.bold('🔗 Ring Network - Two Ring Topology with WebRTC')}
A decentralized network implementation using WebRTC for peer-to-peer communication
in a double ring topology with optional Oracle nodes.
${chalk.green('Usage:')}
npm start - Show this help
npm run start:node [opts] - Start a regular network node
npm run start:oracle [opts] - Start an Oracle node
node node.js [opts] - Start a regular network node directly
node oracle.js [opts] - Start an Oracle node directly
${chalk.yellow('Options:')}
--port <port> Port to listen on (default: random)
--id <id> Node ID (default: auto-generated)
--bootstrap <addr> Bootstrap node address (host:port)
--position <pos> Initial ring position (default: 0)
${chalk.cyan('Examples:')}
# Start first node (bootstrap)
npm run start:oracle -- --port 8080
# Start second node connecting to first
npm run start:node -- --port 8081 --bootstrap localhost:8080
# Start third node (oracle) connecting to network
npm run start:oracle -- --port 8082 --bootstrap localhost:8080
${chalk.magenta('Network Features:')}
✅ Double ring topology (inner and outer rings)
✅ WebRTC peer-to-peer connections
✅ Automatic peer discovery and connection
✅ Message routing through rings
✅ Oracle nodes with enhanced capabilities
✅ Distributed data storage (Oracle)
✅ Consensus mechanisms (Oracle)
✅ Network health monitoring (Oracle)
✅ Advanced routing strategies (Oracle)
${chalk.blue('Oracle Services:')}
🔮 network-analysis - Analyze network topology and health
🔮 data-storage - Distributed data storage with replication
🔮 consensus - Consensus mechanisms for decisions
🔮 routing - Advanced routing strategies
🔮 health-check - Network health monitoring
🔮 network-metrics - Collect and provide metrics
${chalk.red('Quick Start:')}
1. Install dependencies: npm install
2. Start first Oracle: npm run start:oracle -- --port 8080
3. Start regular node: npm run start:node -- --port 8081 --bootstrap localhost:8080
4. Start more nodes by repeating step 3 with different ports
For more information, see the README.md file.
`);
process.exit(0);
}
// If specific arguments are passed, delegate to appropriate script
if (args.includes('--oracle')) {
console.log(chalk.yellow('🔮 Starting Oracle Node...'));
import('./oracle.js');
} else {
console.log(chalk.blue('🔗 Starting Regular Node...'));
import('./node.js');
}

165
node.js Archivo normal
Ver fichero

@@ -0,0 +1,165 @@
#!/usr/bin/env node
import { RingNode } from './src/ring-node.js';
import chalk from 'chalk';
const args = process.argv.slice(2);
const options = {};
// Parse command line arguments
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--port':
options.port = parseInt(args[++i]);
break;
case '--id':
options.id = args[++i];
break;
case '--bootstrap':
options.bootstrap = args[++i];
break;
case '--position':
options.ringPosition = parseInt(args[++i]);
break;
case '--help':
console.log(`
${chalk.blue('Ring Network Node')}
Usage: node node.js [options]
Options:
--port <port> Port to listen on (default: random)
--id <id> Node ID (default: auto-generated)
--bootstrap <addr> Bootstrap node address (host:port)
--position <pos> Initial ring position (default: 0)
--help Show this help message
Examples:
node node.js --port 8080
node node.js --port 8081 --bootstrap localhost:8080
node node.js --id mynode --port 8082 --bootstrap localhost:8080
`);
process.exit(0);
break;
}
}
console.log(chalk.green('🚀 Starting Ring Network Node...'));
const node = new RingNode(options);
// Handle graceful shutdown
process.on('SIGINT', async () => {
console.log(chalk.yellow('\n🛑 Received shutdown signal...'));
await node.destroy();
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log(chalk.yellow('\n🛑 Received termination signal...'));
await node.destroy();
process.exit(0);
});
// Connect to bootstrap node if specified
if (options.bootstrap) {
console.log(chalk.cyan(`🔗 Connecting to bootstrap node: ${options.bootstrap}`));
setTimeout(async () => {
try {
const success = await node.joinRing(options.bootstrap);
if (success) {
console.log(chalk.green('✅ Successfully joined the ring network!'));
} else {
console.log(chalk.red('❌ Failed to join the ring network'));
}
} catch (error) {
console.error(chalk.red('Error joining network:'), error.message);
}
}, 2000);
}
// Set up event handlers for demonstration
node.on('peerConnected', (peerId) => {
console.log(chalk.blue(`👋 New peer connected: ${peerId.substring(0, 8)}`));
});
node.on('peerDisconnected', (peerId) => {
console.log(chalk.red(`👋 Peer disconnected: ${peerId.substring(0, 8)}`));
});
node.on('ringMessage', ({ from, payload }) => {
console.log(chalk.magenta(`📨 Ring message from ${from.substring(0, 8)}: ${JSON.stringify(payload)}`));
});
node.on('networkUpdate', ({ from, payload }) => {
console.log(chalk.cyan(`🔄 Network update from ${from.substring(0, 8)}`));
});
// Interactive commands
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
const chunk = process.stdin.read();
if (chunk !== null) {
const command = chunk.trim();
handleCommand(command);
}
});
function handleCommand(command) {
const [cmd, ...args] = command.split(' ');
switch (cmd) {
case 'send':
if (args.length > 0) {
const message = args.join(' ');
node.sendRingMessage({ type: 'chat', content: message });
console.log(chalk.green(`📤 Sent: ${message}`));
}
break;
case 'info':
const info = node.getNetworkInfo();
console.log(chalk.blue('\n📊 Network Info:'));
console.log(JSON.stringify(info, null, 2));
break;
case 'peers':
const peers = node.webrtc.getConnectedPeers();
console.log(chalk.blue(`\n👥 Connected Peers (${peers.length}):`));
peers.forEach(peer => {
console.log(` - ${peer.substring(0, 8)}...`);
});
break;
case 'help':
console.log(chalk.blue(`
📚 Available Commands:
send <message> - Send a message through the ring
info - Show network information
peers - List connected peers
help - Show this help
quit - Exit the node
`));
break;
case 'quit':
case 'exit':
console.log(chalk.yellow('👋 Goodbye!'));
process.exit(0);
break;
default:
if (command.length > 0) {
console.log(chalk.red(`❓ Unknown command: ${cmd}. Type 'help' for available commands.`));
}
}
}
console.log(chalk.blue(`
🔗 Ring Network Node Started!
Node ID: ${node.id.substring(0, 8)}...
Port: ${node.port}
Type 'help' for available commands.
`));

258
oracle.js Archivo normal
Ver fichero

@@ -0,0 +1,258 @@
#!/usr/bin/env node
import { OracleNode } from './src/oracle-node.js';
import chalk from 'chalk';
const args = process.argv.slice(2);
const options = {};
// Parse command line arguments
for (let i = 0; i < args.length; i++) {
switch (args[i]) {
case '--port':
options.port = parseInt(args[++i]);
break;
case '--id':
options.id = args[++i];
break;
case '--bootstrap':
options.bootstrap = args[++i];
break;
case '--position':
options.ringPosition = parseInt(args[++i]);
break;
case '--help':
console.log(`
${chalk.yellow('Ring Network Oracle Node')}
Usage: node oracle.js [options]
Options:
--port <port> Port to listen on (default: random)
--id <id> Node ID (default: auto-generated)
--bootstrap <addr> Bootstrap node address (host:port)
--position <pos> Initial ring position (default: 0)
--help Show this help message
Examples:
node oracle.js --port 8080
node oracle.js --port 8081 --bootstrap localhost:8080
node oracle.js --id oracle1 --port 8082 --bootstrap localhost:8080
Oracle Services:
- network-analysis: Analyze network topology and health
- data-storage: Distributed data storage with replication
- consensus: Consensus mechanisms for network decisions
- routing: Advanced routing strategies
- health-check: Network health monitoring
- network-metrics: Collect and provide network metrics
`);
process.exit(0);
break;
}
}
console.log(chalk.yellow('🚀 Starting Ring Network Oracle Node...'));
const oracle = new OracleNode(options);
// Handle graceful shutdown
process.on('SIGINT', async () => {
console.log(chalk.yellow('\n🛑 Received shutdown signal...'));
await oracle.destroy();
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log(chalk.yellow('\n🛑 Received termination signal...'));
await oracle.destroy();
process.exit(0);
});
// Connect to bootstrap node if specified
if (options.bootstrap) {
console.log(chalk.cyan(`🔗 Connecting to bootstrap node: ${options.bootstrap}`));
setTimeout(async () => {
try {
const success = await oracle.joinRing(options.bootstrap);
if (success) {
console.log(chalk.green('✅ Successfully joined the ring network as Oracle!'));
} else {
console.log(chalk.red('❌ Failed to join the ring network'));
}
} catch (error) {
console.error(chalk.red('Error joining network:'), error.message);
}
}, 2000);
}
// Set up event handlers
oracle.on('peerConnected', (peerId) => {
console.log(chalk.blue(`👋 New peer connected: ${peerId.substring(0, 8)}`));
});
oracle.on('peerDisconnected', (peerId) => {
console.log(chalk.red(`👋 Peer disconnected: ${peerId.substring(0, 8)}`));
});
oracle.on('ringMessage', ({ from, payload }) => {
console.log(chalk.magenta(`📨 Ring message from ${from.substring(0, 8)}: ${JSON.stringify(payload)}`));
});
oracle.on('oracleResponse', ({ from, payload }) => {
console.log(chalk.cyan(`🔮 Oracle response from ${from.substring(0, 8)}: ${JSON.stringify(payload)}`));
});
// Interactive commands
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
const chunk = process.stdin.read();
if (chunk !== null) {
const command = chunk.trim();
handleCommand(command);
}
});
async function handleCommand(command) {
const [cmd, ...args] = command.split(' ');
switch (cmd) {
case 'send':
if (args.length > 0) {
const message = args.join(' ');
oracle.sendRingMessage({ type: 'chat', content: message });
console.log(chalk.green(`📤 Sent: ${message}`));
}
break;
case 'info':
const info = oracle.getOracleInfo();
console.log(chalk.yellow('\n🔮 Oracle Info:'));
console.log(JSON.stringify(info, null, 2));
break;
case 'peers':
const peers = oracle.webrtc.getConnectedPeers();
console.log(chalk.blue(`\n👥 Connected Peers (${peers.length}):`));
peers.forEach(peer => {
console.log(` - ${peer.substring(0, 8)}...`);
});
break;
case 'health':
const health = oracle.performHealthCheck();
console.log(chalk.green('\n🏥 Health Check:'));
console.log(JSON.stringify(health, null, 2));
break;
case 'metrics':
const metrics = oracle.getNetworkMetrics();
console.log(chalk.cyan('\n📊 Network Metrics:'));
console.log(JSON.stringify(metrics, null, 2));
break;
case 'analyze':
const analysis = oracle.analyzeNetwork();
console.log(chalk.magenta('\n🔍 Network Analysis:'));
console.log(JSON.stringify(analysis, null, 2));
break;
case 'store':
if (args.length >= 2) {
const [key, ...valueParts] = args;
const value = valueParts.join(' ');
const result = oracle.handleDataStorage({
operation: 'set',
key,
value
});
console.log(chalk.green(`💾 Stored: ${key} = ${value}`));
console.log(JSON.stringify(result, null, 2));
} else {
console.log(chalk.red('Usage: store <key> <value>'));
}
break;
case 'get':
if (args.length >= 1) {
const key = args[0];
const result = oracle.handleDataStorage({
operation: 'get',
key
});
console.log(chalk.blue(`📥 Retrieved: ${key}`));
console.log(JSON.stringify(result, null, 2));
} else {
console.log(chalk.red('Usage: get <key>'));
}
break;
case 'propose':
if (args.length >= 1) {
const proposal = args.join(' ');
const proposalId = `proposal-${Date.now()}`;
const result = oracle.handleConsensus({
proposalId,
proposal
});
console.log(chalk.yellow(`📋 Created proposal: ${proposalId}`));
console.log(JSON.stringify(result, null, 2));
} else {
console.log(chalk.red('Usage: propose <proposal text>'));
}
break;
case 'vote':
if (args.length >= 2) {
const [proposalId, vote] = args;
const result = oracle.handleConsensus({
proposalId,
vote
});
console.log(chalk.yellow(`🗳️ Voted ${vote} on ${proposalId}`));
console.log(JSON.stringify(result, null, 2));
} else {
console.log(chalk.red('Usage: vote <proposalId> <yes|no>'));
}
break;
case 'help':
console.log(chalk.yellow(`
🔮 Oracle Commands:
send <msg> - Send a message through the ring
info - Show oracle information
peers - List connected peers
health - Perform health check
metrics - Show network metrics
analyze - Analyze network topology
store <key> <value> - Store data in distributed storage
get <key> - Retrieve data from storage
propose <text> - Create a consensus proposal
vote <id> <yes|no> - Vote on a proposal
help - Show this help
quit - Exit the oracle
`));
break;
case 'quit':
case 'exit':
console.log(chalk.yellow('👋 Oracle shutting down...'));
process.exit(0);
break;
default:
if (command.length > 0) {
console.log(chalk.red(`❓ Unknown command: ${cmd}. Type 'help' for available commands.`));
}
}
}
console.log(chalk.yellow(`
🔮 Ring Network Oracle Node Started!
Oracle ID: ${oracle.id.substring(0, 8)}...
Port: ${oracle.port}
Services: ${Array.from(oracle.oracleServices.keys()).join(', ')}
Type 'help' for available commands.
`));

27
package.json Archivo normal
Ver fichero

@@ -0,0 +1,27 @@
{
"name": "ringnet",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"author": "ale",
"license": "MIT",
"private": true,
"scripts": {
"start": "node index.js",
"start:oracle": "node oracle.js",
"start:node": "node node.js",
"demo": "node demo.js",
"example": "node examples/basic-example.js",
"test": "node test/test-network.js"
},
"dependencies": {
"simple-peer": "^9.11.1",
"ws": "^8.14.2",
"express": "^4.18.2",
"uuid": "^9.0.1",
"chalk": "^5.3.0"
},
"devDependencies": {
"nodemon": "^3.0.2"
}
}

584
src/oracle-node.js Archivo normal
Ver fichero

@@ -0,0 +1,584 @@
import { RingNode } from './ring-node.js';
import chalk from 'chalk';
import { v4 as uuidv4 } from 'uuid';
export class OracleNode extends RingNode {
constructor(options = {}) {
super({
...options,
isOracle: true
});
this.oracleServices = new Map();
this.dataStore = new Map();
this.consensusData = new Map();
this.networkMetrics = {
startTime: Date.now(),
messagesProcessed: 0,
queriesAnswered: 0,
networkEvents: []
};
this.initializeOracleServices();
console.log(chalk.yellow(`🔮✨ Oracle Node ${this.id.substring(0, 8)} initialized with enhanced capabilities`));
}
initializeOracleServices() {
// Register oracle services
this.oracleServices.set('network-analysis', this.analyzeNetwork.bind(this));
this.oracleServices.set('data-storage', this.handleDataStorage.bind(this));
this.oracleServices.set('consensus', this.handleConsensus.bind(this));
this.oracleServices.set('routing', this.handleRouting.bind(this));
this.oracleServices.set('health-check', this.performHealthCheck.bind(this));
this.oracleServices.set('network-metrics', this.getNetworkMetrics.bind(this));
// Start periodic tasks
this.startPeriodicTasks();
}
startPeriodicTasks() {
// Network health monitoring
setInterval(() => {
this.monitorNetworkHealth();
}, 30000); // Every 30 seconds
// Metrics collection
setInterval(() => {
this.collectMetrics();
}, 10000); // Every 10 seconds
// Cleanup old data
setInterval(() => {
this.cleanupOldData();
}, 300000); // Every 5 minutes
}
processOracleQuery(payload) {
const { query, data, service } = payload;
this.networkMetrics.queriesAnswered++;
console.log(chalk.cyan(`🔮 Processing oracle query: ${service || query}`));
if (service && this.oracleServices.has(service)) {
return this.oracleServices.get(service)(data);
}
// Fallback to parent class handling
return super.processOracleQuery(payload);
}
analyzeNetwork(data) {
const connectedPeers = this.webrtc.getConnectedPeers();
const analysis = {
networkSize: this.knownNodes.size + 1, // +1 for this node
connectedPeers: connectedPeers.length,
oracleNodes: this.oracleNodes.size,
ringTopology: {
inner: {
hasLeft: !!this.rings.inner.left,
hasRight: !!this.rings.inner.right,
complete: !!this.rings.inner.left && !!this.rings.inner.right
},
outer: {
hasLeft: !!this.rings.outer.left,
hasRight: !!this.rings.outer.right,
complete: !!this.rings.outer.left && !!this.rings.outer.right
}
},
networkHealth: this.calculateNetworkHealth(),
recommendations: this.generateNetworkRecommendations()
};
return analysis;
}
calculateNetworkHealth() {
const connectedPeers = this.webrtc.getConnectedPeers().length;
const expectedConnections = 4; // 2 for each ring (left and right)
const connectionHealth = Math.min(connectedPeers / expectedConnections, 1.0);
const ringHealth = {
inner: (this.rings.inner.left ? 0.5 : 0) + (this.rings.inner.right ? 0.5 : 0),
outer: (this.rings.outer.left ? 0.5 : 0) + (this.rings.outer.right ? 0.5 : 0)
};
const overallHealth = (connectionHealth + ringHealth.inner + ringHealth.outer) / 3;
return {
overall: Math.round(overallHealth * 100),
connections: Math.round(connectionHealth * 100),
innerRing: Math.round(ringHealth.inner * 100),
outerRing: Math.round(ringHealth.outer * 100)
};
}
generateNetworkRecommendations() {
const recommendations = [];
const health = this.calculateNetworkHealth();
if (health.overall < 70) {
recommendations.push('Network health is low - consider adding more nodes');
}
if (!this.rings.inner.left || !this.rings.inner.right) {
recommendations.push('Inner ring is incomplete - seeking connections');
}
if (!this.rings.outer.left || !this.rings.outer.right) {
recommendations.push('Outer ring is incomplete - seeking connections');
}
if (this.oracleNodes.size < 2) {
recommendations.push('Consider adding more oracle nodes for redundancy');
}
return recommendations;
}
handleDataStorage(data) {
const { operation, key, value, ttl } = data;
switch (operation) {
case 'set':
const entry = {
value,
timestamp: Date.now(),
ttl: ttl || 3600000, // Default 1 hour TTL
nodeId: this.id
};
this.dataStore.set(key, entry);
// Replicate to other oracles for redundancy
this.replicateData(key, entry);
return { success: true, key, timestamp: entry.timestamp };
case 'get':
const storedEntry = this.dataStore.get(key);
if (!storedEntry) {
return { success: false, error: 'Key not found' };
}
// Check TTL
if (Date.now() - storedEntry.timestamp > storedEntry.ttl) {
this.dataStore.delete(key);
return { success: false, error: 'Key expired' };
}
return {
success: true,
key,
value: storedEntry.value,
timestamp: storedEntry.timestamp
};
case 'delete':
const deleted = this.dataStore.delete(key);
if (deleted) {
this.replicateDataDeletion(key);
}
return { success: deleted };
case 'list':
const keys = Array.from(this.dataStore.keys());
return { success: true, keys, count: keys.length };
default:
return { success: false, error: 'Unknown operation' };
}
}
replicateData(key, entry) {
const replicationMessage = {
id: uuidv4(),
type: 'data-replication',
payload: {
operation: 'set',
key,
entry
}
};
// Send to other oracle nodes
this.oracleNodes.forEach(oracleId => {
if (oracleId !== this.id) {
this.webrtc.sendMessage(oracleId, replicationMessage);
}
});
}
replicateDataDeletion(key) {
const replicationMessage = {
id: uuidv4(),
type: 'data-replication',
payload: {
operation: 'delete',
key
}
};
this.oracleNodes.forEach(oracleId => {
if (oracleId !== this.id) {
this.webrtc.sendMessage(oracleId, replicationMessage);
}
});
}
handleConsensus(data) {
const { proposalId, proposal, vote } = data;
if (proposal && !vote) {
// New proposal
this.consensusData.set(proposalId, {
proposal,
votes: new Map(),
timestamp: Date.now(),
status: 'active'
});
// Broadcast proposal to other oracles
this.broadcastConsensusProposal(proposalId, proposal);
return { success: true, proposalId, status: 'proposal_created' };
}
if (vote && proposalId) {
// Vote on existing proposal
const consensusItem = this.consensusData.get(proposalId);
if (!consensusItem) {
return { success: false, error: 'Proposal not found' };
}
consensusItem.votes.set(this.id, vote);
// Broadcast vote
this.broadcastConsensusVote(proposalId, vote);
// Check if consensus reached
const result = this.checkConsensus(proposalId);
return { success: true, proposalId, vote, consensus: result };
}
return { success: false, error: 'Invalid consensus request' };
}
broadcastConsensusProposal(proposalId, proposal) {
const message = {
id: uuidv4(),
type: 'consensus-proposal',
payload: { proposalId, proposal }
};
this.oracleNodes.forEach(oracleId => {
if (oracleId !== this.id) {
this.webrtc.sendMessage(oracleId, message);
}
});
}
broadcastConsensusVote(proposalId, vote) {
const message = {
id: uuidv4(),
type: 'consensus-vote',
payload: { proposalId, vote, voterId: this.id }
};
this.oracleNodes.forEach(oracleId => {
if (oracleId !== this.id) {
this.webrtc.sendMessage(oracleId, message);
}
});
}
checkConsensus(proposalId) {
const consensusItem = this.consensusData.get(proposalId);
if (!consensusItem) return null;
const totalOracles = this.oracleNodes.size + 1; // +1 for this node
const votesNeeded = Math.floor(totalOracles * 0.6) + 1; // 60% majority
const votes = Array.from(consensusItem.votes.values());
const yesVotes = votes.filter(vote => vote === 'yes').length;
const noVotes = votes.filter(vote => vote === 'no').length;
if (yesVotes >= votesNeeded) {
consensusItem.status = 'approved';
return { status: 'approved', votes: { yes: yesVotes, no: noVotes, total: votes.length } };
} else if (noVotes >= votesNeeded) {
consensusItem.status = 'rejected';
return { status: 'rejected', votes: { yes: yesVotes, no: noVotes, total: votes.length } };
}
return { status: 'pending', votes: { yes: yesVotes, no: noVotes, total: votes.length } };
}
handleRouting(data) {
const { destination, message, strategy } = data;
switch (strategy) {
case 'shortest-path':
return this.findShortestPath(destination);
case 'ring-flood':
return this.performRingFlood(message);
case 'oracle-route':
return this.routeThroughOracles(destination, message);
default:
return { success: false, error: 'Unknown routing strategy' };
}
}
findShortestPath(destination) {
// Simple shortest path - in a real implementation, this would use
// algorithms like Dijkstra's or A*
const connectedPeers = this.webrtc.getConnectedPeers();
if (connectedPeers.includes(destination)) {
return {
success: true,
path: [this.id, destination],
hops: 1
};
}
// Check if any connected peer can reach destination
// This is a simplified version - real implementation would maintain
// routing tables and use network topology information
return {
success: false,
error: 'Destination not reachable',
knownPeers: connectedPeers
};
}
performRingFlood(message) {
const flooded = this.sendRingMessage(message, 'both');
return {
success: flooded,
strategy: 'ring-flood',
timestamp: Date.now()
};
}
routeThroughOracles(destination, message) {
const oracleList = Array.from(this.oracleNodes);
let routedCount = 0;
oracleList.forEach(oracleId => {
if (oracleId !== this.id) {
const routingMessage = {
id: uuidv4(),
type: 'oracle-routing',
payload: { destination, message }
};
if (this.webrtc.sendMessage(oracleId, routingMessage)) {
routedCount++;
}
}
});
return {
success: routedCount > 0,
oraclesContacted: routedCount,
strategy: 'oracle-route'
};
}
performHealthCheck(data) {
const checks = {
webrtc: this.webrtc.getConnectedPeers().length > 0,
rings: {
inner: !!this.rings.inner.left || !!this.rings.inner.right,
outer: !!this.rings.outer.left || !!this.rings.outer.right
},
dataStore: this.dataStore.size >= 0,
memory: process.memoryUsage(),
uptime: Date.now() - this.networkMetrics.startTime,
services: Array.from(this.oracleServices.keys())
};
const isHealthy = checks.webrtc && (checks.rings.inner || checks.rings.outer);
return {
healthy: isHealthy,
checks,
timestamp: Date.now(),
nodeId: this.id
};
}
getNetworkMetrics() {
return {
...this.networkMetrics,
dataStoreSize: this.dataStore.size,
consensusItems: this.consensusData.size,
connectedPeers: this.webrtc.getConnectedPeers().length,
knownNodes: this.knownNodes.size,
oracleNodes: this.oracleNodes.size,
memoryUsage: process.memoryUsage(),
uptime: Date.now() - this.networkMetrics.startTime
};
}
monitorNetworkHealth() {
const health = this.performHealthCheck();
this.networkMetrics.networkEvents.push({
type: 'health-check',
timestamp: Date.now(),
data: health
});
if (!health.healthy) {
console.log(chalk.red(`⚠️ Network health issue detected`));
this.handleNetworkHealthIssue(health);
}
}
handleNetworkHealthIssue(health) {
// Attempt to reconnect to peers
if (!health.checks.webrtc) {
console.log(chalk.yellow('🔄 Attempting to reconnect to network...'));
// Implement reconnection logic
}
// Notify other oracles of health issues
const alertMessage = {
id: uuidv4(),
type: 'health-alert',
payload: {
nodeId: this.id,
healthStatus: health,
timestamp: Date.now()
}
};
this.oracleNodes.forEach(oracleId => {
if (oracleId !== this.id) {
this.webrtc.sendMessage(oracleId, alertMessage);
}
});
}
collectMetrics() {
this.networkMetrics.messagesProcessed++;
// Keep only recent events (last 1000)
if (this.networkMetrics.networkEvents.length > 1000) {
this.networkMetrics.networkEvents = this.networkMetrics.networkEvents.slice(-1000);
}
}
cleanupOldData() {
const now = Date.now();
// Clean expired data store entries
for (const [key, entry] of this.dataStore) {
if (now - entry.timestamp > entry.ttl) {
this.dataStore.delete(key);
}
}
// Clean old consensus data (older than 24 hours)
for (const [proposalId, item] of this.consensusData) {
if (now - item.timestamp > 86400000) { // 24 hours
this.consensusData.delete(proposalId);
}
}
console.log(chalk.blue(`🧹 Cleaned up old data. DataStore: ${this.dataStore.size}, Consensus: ${this.consensusData.size}`));
}
// Handle additional message types specific to Oracle
handleMessage(from, message) {
const { type, payload } = message;
switch (type) {
case 'data-replication':
this.handleDataReplication(from, payload);
break;
case 'consensus-proposal':
this.handleConsensusProposal(from, payload);
break;
case 'consensus-vote':
this.handleConsensusVote(from, payload);
break;
case 'health-alert':
this.handleHealthAlert(from, payload);
break;
case 'oracle-routing':
this.handleOracleRouting(from, payload);
break;
default:
super.handleMessage(from, message);
}
}
handleDataReplication(from, payload) {
const { operation, key, entry } = payload;
if (operation === 'set') {
this.dataStore.set(key, entry);
console.log(chalk.blue(`📥 Replicated data: ${key}`));
} else if (operation === 'delete') {
this.dataStore.delete(key);
console.log(chalk.blue(`🗑️ Deleted replicated data: ${key}`));
}
}
handleConsensusProposal(from, payload) {
const { proposalId, proposal } = payload;
this.consensusData.set(proposalId, {
proposal,
votes: new Map(),
timestamp: Date.now(),
status: 'active',
proposer: from
});
console.log(chalk.cyan(`📋 New consensus proposal ${proposalId} from ${from.substring(0, 8)}`));
}
handleConsensusVote(from, payload) {
const { proposalId, vote, voterId } = payload;
const consensusItem = this.consensusData.get(proposalId);
if (consensusItem) {
consensusItem.votes.set(voterId, vote);
const result = this.checkConsensus(proposalId);
console.log(chalk.cyan(`🗳️ Vote received for ${proposalId}: ${vote} (${result.status})`));
}
}
handleHealthAlert(from, payload) {
console.log(chalk.red(`🚨 Health alert from ${from.substring(0, 8)}`));
this.networkMetrics.networkEvents.push({
type: 'health-alert',
timestamp: Date.now(),
from,
data: payload
});
}
handleOracleRouting(from, payload) {
const { destination, message } = payload;
// Attempt to route the message to the destination
if (this.webrtc.sendMessage(destination, message)) {
console.log(chalk.green(`📬 Routed message to ${destination.substring(0, 8)}`));
} else {
console.log(chalk.yellow(`📪 Unable to route message to ${destination.substring(0, 8)}`));
}
}
getOracleInfo() {
return {
...this.getNetworkInfo(),
oracleServices: Array.from(this.oracleServices.keys()),
dataStoreSize: this.dataStore.size,
consensusItems: this.consensusData.size,
metrics: this.getNetworkMetrics()
};
}
}

508
src/ring-node.js Archivo normal
Ver fichero

@@ -0,0 +1,508 @@
import { EventEmitter } from 'events';
import { v4 as uuidv4 } from 'uuid';
import { WebRTCManager } from './webrtc-manager.js';
import WebSocket, { WebSocketServer } from 'ws';
import chalk from 'chalk';
export class RingNode extends EventEmitter {
constructor(options = {}) {
super();
this.id = options.id || uuidv4();
this.port = options.port || this.getRandomPort();
this.ringPosition = options.ringPosition || 0;
this.isOracle = options.isOracle || false;
// Two rings: inner and outer
this.rings = {
inner: {
left: null, // Previous node in inner ring
right: null, // Next node in inner ring
},
outer: {
left: null, // Previous node in outer ring
right: null, // Next node in outer ring
}
};
this.webrtc = new WebRTCManager(this.id);
this.discoveryServer = null;
this.knownNodes = new Map();
this.messageHistory = new Set();
this.oracleNodes = new Set();
this.setupWebRTCHandlers();
this.setupDiscoveryServer();
console.log(chalk.green(`🔗 Ring Node ${this.id.substring(0, 8)} initialized on port ${this.port}`));
if (this.isOracle) {
console.log(chalk.yellow(`🔮 Oracle mode enabled`));
}
}
getRandomPort() {
return Math.floor(Math.random() * (65535 - 49152) + 49152);
}
setupWebRTCHandlers() {
this.webrtc.on('peerConnected', (peerId) => {
console.log(chalk.blue(`✅ Connected to peer: ${peerId.substring(0, 8)}`));
this.emit('peerConnected', peerId);
});
this.webrtc.on('peerDisconnected', (peerId) => {
console.log(chalk.red(`❌ Disconnected from peer: ${peerId.substring(0, 8)}`));
this.handlePeerDisconnection(peerId);
this.emit('peerDisconnected', peerId);
});
this.webrtc.on('message', ({ from, message }) => {
this.handleMessage(from, message);
});
this.webrtc.on('signal', ({ peerId, signal }) => {
this.sendSignalingMessage(peerId, {
type: 'signal',
signal
});
});
}
setupDiscoveryServer() {
this.discoveryServer = new WebSocketServer({ port: this.port });
this.discoveryServer.on('connection', (ws) => {
ws.on('message', async (data) => {
try {
const message = JSON.parse(data.toString());
await this.handleSignalingMessage(ws, message);
} catch (error) {
console.error('Error handling signaling message:', error);
}
});
});
console.log(chalk.cyan(`🌐 Discovery server listening on port ${this.port}`));
}
async handleSignalingMessage(ws, message) {
const { type, from, to, payload } = message;
if (to && to !== this.id) {
// Forward message to the intended recipient
return;
}
switch (type) {
case 'signal':
// Handle WebRTC signaling with simple-peer
let connection = this.webrtc.connections.get(`${this.id}-${from}`);
if (!connection) {
// Create connection as receiver
connection = await this.webrtc.createConnection(from, false);
}
await this.webrtc.handleSignal(from, payload);
break;
case 'discovery':
ws.send(JSON.stringify({
type: 'discovery-response',
nodeId: this.id,
port: this.port,
isOracle: this.isOracle,
ringPosition: this.ringPosition,
connectedPeers: this.webrtc.getConnectedPeers()
}));
break;
case 'join-ring':
await this.handleJoinRingRequest(ws, payload);
break;
}
}
async connectToPeer(nodeId, address) {
try {
const ws = new WebSocket(`ws://${address}`);
return new Promise((resolve, reject) => {
ws.on('open', async () => {
// Send discovery message
ws.send(JSON.stringify({
type: 'discovery',
from: this.id
}));
// Create WebRTC connection as initiator
const connection = await this.webrtc.createConnection(nodeId, true);
// Listen for signaling data from our peer
const signalHandler = ({ peerId, signal }) => {
if (peerId === nodeId) {
ws.send(JSON.stringify({
type: 'signal',
from: this.id,
to: nodeId,
payload: signal
}));
}
};
this.webrtc.on('signal', signalHandler);
// Listen for connection success
const connectHandler = (peerId) => {
if (peerId === nodeId) {
this.webrtc.removeListener('signal', signalHandler);
this.webrtc.removeListener('peerConnected', connectHandler);
ws.close();
resolve(true);
}
};
this.webrtc.on('peerConnected', connectHandler);
});
ws.on('message', async (data) => {
const message = JSON.parse(data.toString());
if (message.type === 'signal' && message.from === nodeId) {
await this.webrtc.handleSignal(nodeId, message.payload);
}
});
ws.on('error', reject);
setTimeout(() => reject(new Error('Connection timeout')), 10000);
});
} catch (error) {
console.error(`Failed to connect to peer ${nodeId}:`, error);
return false;
}
}
async sendSignalingMessage(peerId, message) {
// In a real implementation, this would route through a signaling server
// For now, we'll use direct WebSocket connections
}
handleMessage(from, message) {
const { id: messageId, type, payload, route } = message;
// Prevent message loops
if (this.messageHistory.has(messageId)) {
return;
}
this.messageHistory.add(messageId);
// Clean old message history
if (this.messageHistory.size > 1000) {
const oldMessages = Array.from(this.messageHistory).slice(0, 500);
oldMessages.forEach(id => this.messageHistory.delete(id));
}
console.log(chalk.magenta(`📨 Received ${type} from ${from.substring(0, 8)}`));
switch (type) {
case 'ring-message':
this.handleRingMessage(from, payload, route, messageId);
break;
case 'oracle-query':
if (this.isOracle) {
this.handleOracleQuery(from, payload, messageId);
}
break;
case 'oracle-response':
this.emit('oracleResponse', { from, payload });
break;
case 'network-update':
this.handleNetworkUpdate(from, payload);
break;
default:
this.emit('message', { from, type, payload });
}
}
handleRingMessage(from, payload, route, messageId) {
this.emit('ringMessage', { from, payload });
// Forward message along the ring
this.forwardRingMessage({
id: messageId,
type: 'ring-message',
payload,
route: [...(route || []), this.id]
}, from);
}
forwardRingMessage(message, excludeFrom) {
const route = message.route || [];
// Forward in inner ring
if (this.rings.inner.right && this.rings.inner.right !== excludeFrom) {
this.webrtc.sendMessage(this.rings.inner.right, message);
}
// Forward in outer ring
if (this.rings.outer.right && this.rings.outer.right !== excludeFrom) {
this.webrtc.sendMessage(this.rings.outer.right, message);
}
}
sendRingMessage(payload, ring = 'both') {
const message = {
id: uuidv4(),
type: 'ring-message',
payload,
route: [this.id]
};
let sent = 0;
if (ring === 'inner' || ring === 'both') {
if (this.rings.inner.right) {
if (this.webrtc.sendMessage(this.rings.inner.right, message)) {
sent++;
}
}
}
if (ring === 'outer' || ring === 'both') {
if (this.rings.outer.right) {
if (this.webrtc.sendMessage(this.rings.outer.right, message)) {
sent++;
}
}
}
console.log(chalk.green(`📤 Sent ring message to ${sent} peers`));
return sent > 0;
}
async joinRing(bootstrapNode) {
try {
console.log(chalk.yellow(`🔄 Attempting to join ring via ${bootstrapNode}`));
const success = await this.connectToPeer('bootstrap', bootstrapNode);
if (success) {
// Request to join the ring network
this.sendJoinRingRequest();
return true;
}
return false;
} catch (error) {
console.error('Failed to join ring:', error);
return false;
}
}
sendJoinRingRequest() {
const message = {
id: uuidv4(),
type: 'join-ring',
from: this.id,
payload: {
nodeId: this.id,
port: this.port,
isOracle: this.isOracle
}
};
this.webrtc.broadcast(message);
}
async handleJoinRingRequest(ws, payload) {
const { nodeId, port, isOracle } = payload;
console.log(chalk.cyan(`🔗 New node ${nodeId.substring(0, 8)} wants to join the ring`));
// Add to known nodes
this.knownNodes.set(nodeId, { port, isOracle });
if (isOracle) {
this.oracleNodes.add(nodeId);
}
// Find optimal position in rings for the new node
await this.integrateNewNode(nodeId);
}
async integrateNewNode(nodeId) {
// Simple integration: connect as right neighbor in both rings
// In a production system, this would be more sophisticated
const oldInnerRight = this.rings.inner.right;
const oldOuterRight = this.rings.outer.right;
// Update ring connections
this.rings.inner.right = nodeId;
this.rings.outer.right = nodeId;
// Notify the network of topology change
this.broadcastNetworkUpdate();
console.log(chalk.green(`✅ Integrated node ${nodeId.substring(0, 8)} into rings`));
}
handlePeerDisconnection(peerId) {
// Update ring topology when a peer disconnects
if (this.rings.inner.left === peerId) {
this.rings.inner.left = null;
}
if (this.rings.inner.right === peerId) {
this.rings.inner.right = null;
}
if (this.rings.outer.left === peerId) {
this.rings.outer.left = null;
}
if (this.rings.outer.right === peerId) {
this.rings.outer.right = null;
}
this.knownNodes.delete(peerId);
this.oracleNodes.delete(peerId);
this.broadcastNetworkUpdate();
}
broadcastNetworkUpdate() {
const message = {
id: uuidv4(),
type: 'network-update',
payload: {
nodeId: this.id,
rings: this.rings,
knownNodes: Array.from(this.knownNodes.keys()),
oracleNodes: Array.from(this.oracleNodes)
}
};
this.webrtc.broadcast(message);
}
handleNetworkUpdate(from, payload) {
const { rings, knownNodes, oracleNodes } = payload;
// Update our knowledge of network topology
knownNodes.forEach(nodeId => {
if (!this.knownNodes.has(nodeId)) {
this.knownNodes.set(nodeId, {});
}
});
oracleNodes.forEach(nodeId => {
this.oracleNodes.add(nodeId);
});
this.emit('networkUpdate', { from, payload });
}
// Oracle-specific methods
handleOracleQuery(from, payload, messageId) {
console.log(chalk.yellow(`🔮 Oracle query from ${from.substring(0, 8)}: ${payload.query}`));
// Process oracle query and send response
const response = this.processOracleQuery(payload);
const responseMessage = {
id: uuidv4(),
type: 'oracle-response',
payload: {
queryId: messageId,
response,
timestamp: Date.now()
}
};
this.webrtc.sendMessage(from, responseMessage);
}
processOracleQuery(payload) {
const { query, data } = payload;
// Simple oracle responses - extend this for your specific use case
switch (query) {
case 'network-status':
return {
connectedPeers: this.webrtc.getConnectedPeers().length,
knownNodes: this.knownNodes.size,
oracleNodes: this.oracleNodes.size,
rings: this.rings
};
case 'timestamp':
return { timestamp: Date.now() };
case 'echo':
return { echo: data };
default:
return { error: 'Unknown query type' };
}
}
queryOracle(query, data = null) {
const oracleList = Array.from(this.oracleNodes);
if (oracleList.length === 0) {
return Promise.reject(new Error('No oracle nodes available'));
}
// Query first available oracle
const oracleId = oracleList[0];
const message = {
id: uuidv4(),
type: 'oracle-query',
payload: { query, data }
};
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Oracle query timeout'));
}, 5000);
const responseHandler = ({ from, payload }) => {
if (from === oracleId) {
clearTimeout(timeout);
this.removeListener('oracleResponse', responseHandler);
resolve(payload.response);
}
};
this.on('oracleResponse', responseHandler);
if (!this.webrtc.sendMessage(oracleId, message)) {
clearTimeout(timeout);
this.removeListener('oracleResponse', responseHandler);
reject(new Error('Failed to send oracle query'));
}
});
}
getNetworkInfo() {
return {
nodeId: this.id,
port: this.port,
isOracle: this.isOracle,
rings: this.rings,
connectedPeers: this.webrtc.getConnectedPeers(),
knownNodes: Array.from(this.knownNodes.keys()),
oracleNodes: Array.from(this.oracleNodes)
};
}
async destroy() {
console.log(chalk.red(`🛑 Shutting down node ${this.id.substring(0, 8)}`));
// Notify peers of shutdown
this.webrtc.broadcast({
id: uuidv4(),
type: 'node-shutdown',
payload: { nodeId: this.id }
});
// Clean up connections
this.webrtc.destroy();
if (this.discoveryServer) {
this.discoveryServer.close();
}
this.removeAllListeners();
}
}

160
src/webrtc-manager.js Archivo normal
Ver fichero

@@ -0,0 +1,160 @@
import SimplePeer from 'simple-peer';
import { EventEmitter } from 'events';
import { v4 as uuidv4 } from 'uuid';
export class WebRTCManager extends EventEmitter {
constructor(nodeId) {
super();
this.nodeId = nodeId;
this.connections = new Map();
this.pendingConnections = new Map();
}
async createConnection(peerId, isInitiator = false) {
const connectionId = `${this.nodeId}-${peerId}`;
if (this.connections.has(connectionId)) {
return this.connections.get(connectionId);
}
const peer = new SimplePeer({
initiator: isInitiator,
trickle: false,
config: {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' }
]
}
});
const connection = {
id: connectionId,
peerId,
peer,
isInitiator,
state: 'connecting'
};
// Handle signaling data
peer.on('signal', (data) => {
this.emit('signal', {
peerId,
signal: data
});
});
// Handle connection
peer.on('connect', () => {
connection.state = 'connected';
console.log(`WebRTC connection established with ${peerId.substring(0, 8)}`);
this.emit('peerConnected', peerId);
});
// Handle data
peer.on('data', (data) => {
try {
const message = JSON.parse(data.toString());
this.emit('message', {
from: peerId,
message
});
} catch (error) {
console.error('Error parsing message:', error);
}
});
// Handle errors
peer.on('error', (error) => {
console.error(`WebRTC error with ${peerId}:`, error.message);
connection.state = 'failed';
this.removePeer(peerId);
});
// Handle close
peer.on('close', () => {
console.log(`WebRTC connection closed with ${peerId.substring(0, 8)}`);
connection.state = 'disconnected';
this.removePeer(peerId);
});
this.connections.set(connectionId, connection);
return connection;
}
async handleSignal(peerId, signal) {
const connectionId = `${this.nodeId}-${peerId}`;
const connection = this.connections.get(connectionId);
if (connection && connection.peer) {
connection.peer.signal(signal);
}
}
sendMessage(peerId, message) {
const connectionId = `${this.nodeId}-${peerId}`;
const connection = this.connections.get(connectionId);
if (connection && connection.peer && connection.peer.connected) {
try {
connection.peer.send(JSON.stringify(message));
return true;
} catch (error) {
console.error(`Error sending message to ${peerId}:`, error);
return false;
}
}
return false;
}
broadcast(message, excludePeers = []) {
let sent = 0;
for (const [connectionId, connection] of this.connections) {
if (!excludePeers.includes(connection.peerId) &&
connection.peer &&
connection.peer.connected) {
try {
connection.peer.send(JSON.stringify(message));
sent++;
} catch (error) {
console.error(`Error broadcasting to ${connection.peerId}:`, error);
}
}
}
return sent;
}
removePeer(peerId) {
const connectionId = `${this.nodeId}-${peerId}`;
const connection = this.connections.get(connectionId);
if (connection) {
if (connection.peer) {
connection.peer.destroy();
}
this.connections.delete(connectionId);
this.emit('peerDisconnected', peerId);
}
}
getConnectedPeers() {
return Array.from(this.connections.values())
.filter(conn => conn.peer && conn.peer.connected)
.map(conn => conn.peerId);
}
getConnectionState(peerId) {
const connectionId = `${this.nodeId}-${peerId}`;
const connection = this.connections.get(connectionId);
return connection ? connection.state : 'disconnected';
}
destroy() {
for (const [connectionId, connection] of this.connections) {
if (connection.peer) {
connection.peer.destroy();
}
}
this.connections.clear();
}
}

339
test/test-network.js Archivo normal
Ver fichero

@@ -0,0 +1,339 @@
#!/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 };