feat: implement automatic ring positioning
- Remove manual --position parameter from CLI - Add automatic optimal position calculation based on gap analysis - Implement dynamic ring topology management - Add position tracking and synchronization across nodes - Add topology command for visual ring structure inspection - Clean up unused variables and dead code - Simplify node setup with automatic positioning
Este commit está contenido en:
30
README.md
30
README.md
@@ -77,12 +77,13 @@ npm run start:node -- --port 8083 --bootstrap localhost:8080
|
||||
--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)
|
||||
--ice-servers <json> # ICE servers configuration (JSON array)
|
||||
--config <file> # Load configuration from JSON file
|
||||
--help # Show help message
|
||||
```
|
||||
|
||||
**Note**: Ring positions are now assigned automatically for optimal network topology. The system calculates the best position for each node to ensure even distribution around the ring.
|
||||
|
||||
### WebRTC ICE Servers Configuration
|
||||
|
||||
The Ring Network uses WebRTC for peer-to-peer connections. You can configure custom ICE servers (STUN/TURN) for better connectivity:
|
||||
@@ -137,6 +138,7 @@ Once a node is running, you can use these commands:
|
||||
- `info` - Show network information
|
||||
- `peers` - List connected peers
|
||||
- `connections` - Show persistent connection status
|
||||
- `topology` - Show ring topology and node positions
|
||||
- `help` - Show available commands
|
||||
- `quit` - Exit the node
|
||||
|
||||
@@ -148,6 +150,7 @@ Once a node is running, you can use these commands:
|
||||
- `get <key>` - Retrieve data from storage
|
||||
- `propose <text>` - Create a consensus proposal
|
||||
- `vote <id> <yes|no>` - Vote on a proposal
|
||||
- `topology` - Show ring topology and node positions
|
||||
|
||||
## 🔮 Oracle Services
|
||||
|
||||
@@ -183,6 +186,31 @@ Oracle nodes provide enhanced services to the network:
|
||||
- Usage analytics
|
||||
- Historical data tracking
|
||||
|
||||
## 🎯 Automatic Ring Positioning
|
||||
|
||||
The Ring Network now features **automatic node positioning** that optimizes network topology without manual configuration:
|
||||
|
||||
### Key Features
|
||||
- **Optimal Placement**: New nodes are automatically positioned in the largest gap between existing nodes
|
||||
- **Even Distribution**: Nodes are distributed evenly around the virtual ring for balanced load
|
||||
- **Dynamic Rebalancing**: Network topology adjusts automatically when nodes join or leave
|
||||
- **No Manual Configuration**: Eliminates the need to manually specify ring positions
|
||||
|
||||
### How It Works
|
||||
1. **Virtual Ring**: The network uses a virtual ring of 1000 positions
|
||||
2. **Gap Analysis**: When a new node joins, the system finds the largest gap between existing nodes
|
||||
3. **Optimal Insertion**: The new node is placed in the middle of the largest gap
|
||||
4. **Topology Update**: All nodes update their neighbor connections based on the new topology
|
||||
5. **Automatic Rebalancing**: The system can redistribute nodes evenly when needed
|
||||
|
||||
### Benefits
|
||||
- **Simplified Setup**: No need to calculate or specify positions manually
|
||||
- **Better Performance**: Optimal positioning improves routing efficiency
|
||||
- **Self-Healing**: Network automatically adapts to node changes
|
||||
- **Scalability**: Easy to add new nodes without topology planning
|
||||
|
||||
Use the `topology` command in any node to view the current ring structure and positions.
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
Run the test suite to verify network functionality:
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"iceServers": [
|
||||
{
|
||||
"urls": "stun:stun.l.google.com:19302"
|
||||
},
|
||||
{
|
||||
"urls": "stun:stun1.l.google.com:19302"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"iceServers": [
|
||||
{
|
||||
"urls": [
|
||||
"stun:stun.l.google.com:19302",
|
||||
"stun:stun1.l.google.com:19302",
|
||||
"stun:stun2.l.google.com:19302"
|
||||
]
|
||||
},
|
||||
{
|
||||
"urls": "turn:openrelay.metered.ca:80",
|
||||
"username": "openrelayproject",
|
||||
"credential": "openrelayproject"
|
||||
},
|
||||
{
|
||||
"urls": "turn:openrelay.metered.ca:443",
|
||||
"username": "openrelayproject",
|
||||
"credential": "openrelayproject"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"iceServers": [
|
||||
{
|
||||
"urls": "stun:stun.l.google.com:19302"
|
||||
},
|
||||
{
|
||||
"urls": "stun:stun1.l.google.com:19302"
|
||||
},
|
||||
{
|
||||
"urls": "turn:turnserver.example.com:3478",
|
||||
"username": "your_username",
|
||||
"credential": "your_password"
|
||||
}
|
||||
]
|
||||
}
|
||||
32
node.js
32
node.js
@@ -19,9 +19,6 @@ for (let i = 0; i < args.length; i++) {
|
||||
case '--bootstrap':
|
||||
options.bootstrap = args[++i];
|
||||
break;
|
||||
case '--position':
|
||||
options.ringPosition = parseInt(args[++i]);
|
||||
break;
|
||||
case '--ice-servers':
|
||||
try {
|
||||
options.iceServers = JSON.parse(args[++i]);
|
||||
@@ -52,7 +49,6 @@ 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)
|
||||
--ice-servers <json> ICE servers configuration (JSON array)
|
||||
--config <file> Load configuration from JSON file
|
||||
--help Show this help message
|
||||
@@ -62,6 +58,8 @@ Examples:
|
||||
node node.js --port 8081 --bootstrap localhost:8080
|
||||
node node.js --id mynode --port 8082 --bootstrap localhost:8080
|
||||
node node.js --config config/ice-servers-with-turn.json --port 8080
|
||||
|
||||
Note: Ring positions are now assigned automatically for optimal network topology.
|
||||
|
||||
ICE Servers Example:
|
||||
--ice-servers '[{"urls":"stun:stun.l.google.com:19302"},{"urls":"turn:turn.example.com:3478","username":"user","credential":"pass"}]'
|
||||
@@ -175,6 +173,31 @@ function handleCommand(command) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'topology':
|
||||
const topology = node.getRingTopology();
|
||||
console.log(chalk.blue('\n🔄 Ring Topology:'));
|
||||
console.log(` Ring Size: ${topology.ringSize}`);
|
||||
console.log(` Total Nodes: ${topology.totalNodes}`);
|
||||
console.log(` My Position: ${node.ringPosition}`);
|
||||
|
||||
if (topology.nodes.length > 0) {
|
||||
console.log(chalk.yellow('\n📍 Node Positions:'));
|
||||
topology.nodes.forEach(nodeInfo => {
|
||||
const indicators = [];
|
||||
if (nodeInfo.isThisNode) indicators.push(chalk.green('ME'));
|
||||
if (nodeInfo.isOracle) indicators.push(chalk.yellow('🔮'));
|
||||
if (nodeInfo.isConnected) indicators.push(chalk.green('✓'));
|
||||
|
||||
const indicatorStr = indicators.length > 0 ? ` [${indicators.join(' ')}]` : '';
|
||||
console.log(` ${nodeInfo.position.toString().padStart(4)}: ${nodeInfo.nodeId}...${indicatorStr}`);
|
||||
});
|
||||
|
||||
console.log(chalk.cyan('\n🔄 Ring Connections:'));
|
||||
console.log(` Inner Ring: ${topology.innerRing.left}... ← ME → ${topology.innerRing.right}...`);
|
||||
console.log(` Outer Ring: ${topology.outerRing.left}... ← ME → ${topology.outerRing.right}...`);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'help':
|
||||
console.log(chalk.blue(`
|
||||
📚 Available Commands:
|
||||
@@ -182,6 +205,7 @@ function handleCommand(command) {
|
||||
info - Show network information
|
||||
peers - List connected peers
|
||||
connections - Show persistent connection status
|
||||
topology - Show ring topology and positions
|
||||
help - Show this help
|
||||
quit - Exit the node
|
||||
`));
|
||||
|
||||
32
oracle.js
32
oracle.js
@@ -19,9 +19,6 @@ for (let i = 0; i < args.length; i++) {
|
||||
case '--bootstrap':
|
||||
options.bootstrap = args[++i];
|
||||
break;
|
||||
case '--position':
|
||||
options.ringPosition = parseInt(args[++i]);
|
||||
break;
|
||||
case '--ice-servers':
|
||||
try {
|
||||
options.iceServers = JSON.parse(args[++i]);
|
||||
@@ -52,7 +49,6 @@ 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)
|
||||
--ice-servers <json> ICE servers configuration (JSON array)
|
||||
--config <file> Load configuration from JSON file
|
||||
--help Show this help message
|
||||
@@ -63,6 +59,8 @@ Examples:
|
||||
node oracle.js --id oracle1 --port 8082 --bootstrap localhost:8080
|
||||
node oracle.js --config config/ice-servers-with-turn.json --port 8080
|
||||
|
||||
Note: Ring positions are now assigned automatically for optimal network topology.
|
||||
|
||||
ICE Servers Example:
|
||||
--ice-servers '[{"urls":"stun:stun.l.google.com:19302"},{"urls":"turn:turn.example.com:3478","username":"user","credential":"pass"}]'
|
||||
|
||||
@@ -240,6 +238,31 @@ async function handleCommand(command) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'topology':
|
||||
const topology = oracle.getRingTopology();
|
||||
console.log(chalk.blue('\n🔄 Ring Topology:'));
|
||||
console.log(` Ring Size: ${topology.ringSize}`);
|
||||
console.log(` Total Nodes: ${topology.totalNodes}`);
|
||||
console.log(` My Position: ${oracle.ringPosition}`);
|
||||
|
||||
if (topology.nodes.length > 0) {
|
||||
console.log(chalk.yellow('\n📍 Node Positions:'));
|
||||
topology.nodes.forEach(nodeInfo => {
|
||||
const indicators = [];
|
||||
if (nodeInfo.isThisNode) indicators.push(chalk.green('ME'));
|
||||
if (nodeInfo.isOracle) indicators.push(chalk.yellow('🔮'));
|
||||
if (nodeInfo.isConnected) indicators.push(chalk.green('✓'));
|
||||
|
||||
const indicatorStr = indicators.length > 0 ? ` [${indicators.join(' ')}]` : '';
|
||||
console.log(` ${nodeInfo.position.toString().padStart(4)}: ${nodeInfo.nodeId}...${indicatorStr}`);
|
||||
});
|
||||
|
||||
console.log(chalk.cyan('\n🔄 Ring Connections:'));
|
||||
console.log(` Inner Ring: ${topology.innerRing.left}... ← ME → ${topology.innerRing.right}...`);
|
||||
console.log(` Outer Ring: ${topology.outerRing.left}... ← ME → ${topology.outerRing.right}...`);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'help':
|
||||
console.log(chalk.yellow(`
|
||||
🔮 Oracle Commands:
|
||||
@@ -253,6 +276,7 @@ async function handleCommand(command) {
|
||||
get <key> - Retrieve data from storage
|
||||
propose <text> - Create a consensus proposal
|
||||
vote <id> <yes|no> - Vote on a proposal
|
||||
topology - Show ring topology and positions
|
||||
help - Show this help
|
||||
quit - Exit the oracle
|
||||
`));
|
||||
|
||||
246
src/ring-node.js
246
src/ring-node.js
@@ -9,7 +9,7 @@ export class RingNode extends EventEmitter {
|
||||
super();
|
||||
this.id = options.id || uuidv4();
|
||||
this.port = options.port || this.getRandomPort();
|
||||
this.ringPosition = options.ringPosition || 0;
|
||||
this.ringPosition = null; // Will be assigned automatically
|
||||
this.isOracle = options.isOracle || false;
|
||||
|
||||
// Two rings: inner and outer
|
||||
@@ -32,6 +32,11 @@ export class RingNode extends EventEmitter {
|
||||
this.messageHistory = new Set();
|
||||
this.oracleNodes = new Set();
|
||||
this.activeSignalingConnections = new Map(); // Store active WebSocket connections for signaling
|
||||
this.nodePositions = new Map(); // Track all node positions in the ring
|
||||
this.ringSize = 1000; // Virtual ring size for position calculation
|
||||
|
||||
// Assign initial position for this node
|
||||
this.assignPosition(this.id);
|
||||
|
||||
this.setupWebRTCHandlers();
|
||||
this.setupDiscoveryServer();
|
||||
@@ -395,6 +400,21 @@ export class RingNode extends EventEmitter {
|
||||
if (message.success) {
|
||||
bootstrapNodeId = message.bootstrapNodeId;
|
||||
|
||||
// Update position if provided by bootstrap node
|
||||
if (message.assignedPosition !== undefined) {
|
||||
this.ringPosition = message.assignedPosition;
|
||||
this.nodePositions.set(this.id, message.assignedPosition);
|
||||
console.log(chalk.green(`📍 Assigned position ${message.assignedPosition} in the ring`));
|
||||
}
|
||||
|
||||
// Update network topology if provided
|
||||
if (message.networkTopology) {
|
||||
Object.entries(message.networkTopology).forEach(([nodeId, position]) => {
|
||||
this.nodePositions.set(nodeId, position);
|
||||
});
|
||||
this.updateRingTopology();
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
ws.close();
|
||||
resolve(true);
|
||||
@@ -450,16 +470,18 @@ export class RingNode extends EventEmitter {
|
||||
this.oracleNodes.add(nodeId);
|
||||
}
|
||||
|
||||
// Find optimal position in rings for the new node
|
||||
await this.integrateNewNode(nodeId);
|
||||
// Find optimal position for the new node
|
||||
const assignedPosition = await this.integrateNewNode(nodeId);
|
||||
|
||||
// Send success response with bootstrap node info
|
||||
// Send success response with position and topology info
|
||||
ws.send(JSON.stringify({
|
||||
type: 'join-response',
|
||||
success: true,
|
||||
message: 'Successfully integrated into ring network',
|
||||
bootstrapNodeId: this.id,
|
||||
isOracle: this.isOracle
|
||||
isOracle: this.isOracle,
|
||||
assignedPosition: assignedPosition,
|
||||
networkTopology: Object.fromEntries(this.nodePositions)
|
||||
}));
|
||||
|
||||
// After successful join, try to establish WebRTC connection
|
||||
@@ -490,41 +512,152 @@ export class RingNode extends EventEmitter {
|
||||
message: 'Failed to integrate into ring network'
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
async integrateNewNode(nodeId) {
|
||||
// Simple integration: connect as right neighbor in both rings
|
||||
// In a production system, this would be more sophisticated
|
||||
} async integrateNewNode(nodeId) {
|
||||
// Automatically assign optimal position for the new node
|
||||
const newPosition = this.calculateOptimalPosition();
|
||||
this.nodePositions.set(nodeId, newPosition);
|
||||
|
||||
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;
|
||||
// Update ring topology with the new node
|
||||
this.updateRingTopology();
|
||||
|
||||
// Notify the network of topology change
|
||||
this.broadcastNetworkUpdate();
|
||||
|
||||
console.log(chalk.green(`🔄 Integrated node ${nodeId} at position ${newPosition}`));
|
||||
|
||||
return newPosition;
|
||||
}
|
||||
|
||||
// Automatic position management methods
|
||||
calculateOptimalPosition() {
|
||||
// If this is the first node in the ring
|
||||
if (this.nodePositions.size === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If there's only one node, place the new one opposite to it
|
||||
if (this.nodePositions.size === 1) {
|
||||
return Math.floor(this.ringSize / 2);
|
||||
}
|
||||
|
||||
// Get all current positions and sort them
|
||||
const positions = Array.from(this.nodePositions.values()).sort((a, b) => a - b);
|
||||
|
||||
// Find the largest gap between consecutive positions
|
||||
let largestGap = 0;
|
||||
let optimalPosition = 0;
|
||||
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
const currentPos = positions[i];
|
||||
const nextPos = positions[(i + 1) % positions.length];
|
||||
|
||||
// Calculate gap (considering ring wraparound)
|
||||
let gap;
|
||||
if (nextPos > currentPos) {
|
||||
gap = nextPos - currentPos;
|
||||
} else {
|
||||
// Wraparound case
|
||||
gap = (this.ringSize - currentPos) + nextPos;
|
||||
}
|
||||
|
||||
if (gap > largestGap) {
|
||||
largestGap = gap;
|
||||
// Place new node in the middle of the largest gap
|
||||
optimalPosition = (currentPos + gap / 2) % this.ringSize;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.floor(optimalPosition);
|
||||
}
|
||||
|
||||
assignPosition(nodeId, position = null) {
|
||||
if (position === null) {
|
||||
position = this.calculateOptimalPosition();
|
||||
}
|
||||
|
||||
this.nodePositions.set(nodeId, position);
|
||||
|
||||
if (nodeId === this.id) {
|
||||
this.ringPosition = position;
|
||||
}
|
||||
|
||||
// Update ring topology based on new positions
|
||||
this.updateRingTopology();
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
updateRingTopology() {
|
||||
// Sort all nodes by their positions
|
||||
const sortedNodes = Array.from(this.nodePositions.entries())
|
||||
.sort((a, b) => a[1] - b[1]); // Sort by position
|
||||
|
||||
// Clear current ring connections
|
||||
this.rings.inner.left = null;
|
||||
this.rings.inner.right = null;
|
||||
this.rings.outer.left = null;
|
||||
this.rings.outer.right = null;
|
||||
|
||||
if (sortedNodes.length <= 1) {
|
||||
return; // Not enough nodes for a ring
|
||||
}
|
||||
|
||||
// Find this node's index in the sorted list
|
||||
const thisNodeIndex = sortedNodes.findIndex(([nodeId]) => nodeId === this.id);
|
||||
|
||||
if (thisNodeIndex === -1) {
|
||||
return; // This node not found in positions
|
||||
}
|
||||
|
||||
const nodeCount = sortedNodes.length;
|
||||
|
||||
// Calculate neighbors in inner ring (clockwise)
|
||||
const innerRightIndex = (thisNodeIndex + 1) % nodeCount;
|
||||
const innerLeftIndex = (thisNodeIndex - 1 + nodeCount) % nodeCount;
|
||||
|
||||
this.rings.inner.right = sortedNodes[innerRightIndex][0];
|
||||
this.rings.inner.left = sortedNodes[innerLeftIndex][0];
|
||||
|
||||
// Calculate neighbors in outer ring (counter-clockwise)
|
||||
this.rings.outer.right = sortedNodes[innerLeftIndex][0];
|
||||
this.rings.outer.left = sortedNodes[innerRightIndex][0];
|
||||
}
|
||||
|
||||
removeNodePosition(nodeId) {
|
||||
this.nodePositions.delete(nodeId);
|
||||
this.updateRingTopology();
|
||||
}
|
||||
|
||||
rebalanceRing() {
|
||||
// Redistribute all nodes evenly around the ring
|
||||
const nodeIds = Array.from(this.nodePositions.keys());
|
||||
const nodeCount = nodeIds.length;
|
||||
|
||||
if (nodeCount === 0) return;
|
||||
|
||||
const step = this.ringSize / nodeCount;
|
||||
|
||||
nodeIds.forEach((nodeId, index) => {
|
||||
const newPosition = Math.floor(index * step);
|
||||
this.nodePositions.set(nodeId, newPosition);
|
||||
|
||||
if (nodeId === this.id) {
|
||||
this.ringPosition = newPosition;
|
||||
}
|
||||
});
|
||||
|
||||
this.updateRingTopology();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// Remove node position and update topology
|
||||
this.removeNodePosition(peerId);
|
||||
|
||||
this.knownNodes.delete(peerId);
|
||||
this.oracleNodes.delete(peerId);
|
||||
|
||||
console.log(chalk.yellow(`🔄 Node ${peerId} disconnected, ring topology updated`));
|
||||
|
||||
this.broadcastNetworkUpdate();
|
||||
}
|
||||
|
||||
@@ -536,7 +669,9 @@ export class RingNode extends EventEmitter {
|
||||
nodeId: this.id,
|
||||
rings: this.rings,
|
||||
knownNodes: Array.from(this.knownNodes.keys()),
|
||||
oracleNodes: Array.from(this.oracleNodes)
|
||||
oracleNodes: Array.from(this.oracleNodes),
|
||||
nodePositions: Object.fromEntries(this.nodePositions),
|
||||
ringPosition: this.ringPosition
|
||||
}
|
||||
};
|
||||
|
||||
@@ -544,7 +679,7 @@ export class RingNode extends EventEmitter {
|
||||
}
|
||||
|
||||
handleNetworkUpdate(from, payload) {
|
||||
const { rings, knownNodes, oracleNodes } = payload;
|
||||
const { rings, knownNodes, oracleNodes, nodePositions, ringPosition } = payload;
|
||||
|
||||
// Update our knowledge of network topology
|
||||
knownNodes.forEach(nodeId => {
|
||||
@@ -557,6 +692,24 @@ export class RingNode extends EventEmitter {
|
||||
this.oracleNodes.add(nodeId);
|
||||
});
|
||||
|
||||
// Update node positions if provided
|
||||
if (nodePositions) {
|
||||
// Merge position information from other nodes
|
||||
Object.entries(nodePositions).forEach(([nodeId, position]) => {
|
||||
if (!this.nodePositions.has(nodeId)) {
|
||||
this.nodePositions.set(nodeId, position);
|
||||
}
|
||||
});
|
||||
|
||||
// Update the sender's position
|
||||
if (ringPosition !== undefined) {
|
||||
this.nodePositions.set(from, ringPosition);
|
||||
}
|
||||
|
||||
// Recalculate our ring topology based on updated positions
|
||||
this.updateRingTopology();
|
||||
}
|
||||
|
||||
this.emit('networkUpdate', { from, payload });
|
||||
}
|
||||
|
||||
@@ -641,10 +794,13 @@ export class RingNode extends EventEmitter {
|
||||
nodeId: this.id,
|
||||
port: this.port,
|
||||
isOracle: this.isOracle,
|
||||
ringPosition: this.ringPosition,
|
||||
rings: this.rings,
|
||||
connectedPeers: this.webrtc.getConnectedPeers(),
|
||||
knownNodes: Array.from(this.knownNodes.keys()),
|
||||
oracleNodes: Array.from(this.oracleNodes)
|
||||
oracleNodes: Array.from(this.oracleNodes),
|
||||
nodePositions: Object.fromEntries(this.nodePositions),
|
||||
ringTopology: this.getRingTopology()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -765,4 +921,30 @@ export class RingNode extends EventEmitter {
|
||||
oracleNodes: this.oracleNodes.size
|
||||
};
|
||||
}
|
||||
|
||||
getRingTopology() {
|
||||
// Create a visual representation of the ring topology
|
||||
const sortedNodes = Array.from(this.nodePositions.entries())
|
||||
.sort((a, b) => a[1] - b[1]); // Sort by position
|
||||
|
||||
return {
|
||||
totalNodes: sortedNodes.length,
|
||||
ringSize: this.ringSize,
|
||||
nodes: sortedNodes.map(([nodeId, position]) => ({
|
||||
nodeId: nodeId.substring(0, 8), // Short version for display
|
||||
position: position,
|
||||
isThisNode: nodeId === this.id,
|
||||
isOracle: this.oracleNodes.has(nodeId),
|
||||
isConnected: this.webrtc.getConnectedPeers().includes(nodeId)
|
||||
})),
|
||||
innerRing: {
|
||||
left: this.rings.inner.left?.substring(0, 8),
|
||||
right: this.rings.inner.right?.substring(0, 8)
|
||||
},
|
||||
outerRing: {
|
||||
left: this.rings.outer.left?.substring(0, 8),
|
||||
right: this.rings.outer.right?.substring(0, 8)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Referencia en una nueva incidencia
Block a user