Files
aitbc/apps/blockchain-node/src/aitbc_chain/network/island_manager.py
aitbc fefa6c4435
Some checks failed
CLI Tests / test-cli (push) Has been cancelled
Documentation Validation / validate-docs (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
config: add island federation and NAT traversal support for federated mesh architecture
- Add island configuration fields (island_id, island_name, is_hub, island_chain_id, hub_discovery_url, bridge_islands)
- Add NAT traversal configuration (STUN/TURN servers and credentials)
- Add DEFAULT_ISLAND_ID using UUID for new installations
- Extend PeerNode with public_address, public_port, island_id, island_chain_id, and is_hub fields
- Update DiscoveryMessage to include island metadata and public endpoint
2026-04-13 08:57:34 +02:00

309 lines
11 KiB
Python

"""
Island Manager
Manages island membership, multi-island support, and island operations for federated mesh
"""
import asyncio
import logging
import uuid
from typing import Dict, List, Optional, Set
from dataclasses import dataclass, field
from enum import Enum
import time
logger = logging.getLogger(__name__)
class IslandStatus(Enum):
"""Island membership status"""
ACTIVE = "active"
INACTIVE = "inactive"
BRIDGING = "bridging"
@dataclass
class IslandMembership:
"""Represents a node's membership in an island"""
island_id: str
island_name: str
chain_id: str
status: IslandStatus
joined_at: float
is_hub: bool = False
peer_count: int = 0
@dataclass
class BridgeRequest:
"""Represents a bridge request to another island"""
request_id: str
source_island_id: str
target_island_id: str
source_node_id: str
timestamp: float
status: str = "pending" # pending, approved, rejected
class IslandManager:
"""Manages island membership and operations for federated mesh"""
def __init__(self, local_node_id: str, default_island_id: str, default_chain_id: str):
self.local_node_id = local_node_id
self.default_island_id = default_island_id
self.default_chain_id = default_chain_id
# Island memberships
self.islands: Dict[str, IslandMembership] = {}
# Bridge requests
self.bridge_requests: Dict[str, BridgeRequest] = {}
# Active bridges
self.active_bridges: Set[str] = set() # island_ids we're bridged to
# Island peers (island_id -> set of node_ids)
self.island_peers: Dict[str, Set[str]] = {}
self.running = False
# Initialize with default island
self._initialize_default_island()
def _initialize_default_island(self):
"""Initialize with default island membership"""
self.islands[self.default_island_id] = IslandMembership(
island_id=self.default_island_id,
island_name="default",
chain_id=self.default_chain_id,
status=IslandStatus.ACTIVE,
joined_at=time.time(),
is_hub=False
)
self.island_peers[self.default_island_id] = set()
logger.info(f"Initialized with default island: {self.default_island_id}")
async def start(self):
"""Start island manager"""
self.running = True
logger.info(f"Starting island manager for node {self.local_node_id}")
# Start background tasks
tasks = [
asyncio.create_task(self._bridge_request_monitor()),
asyncio.create_task(self._island_health_check())
]
try:
await asyncio.gather(*tasks)
except Exception as e:
logger.error(f"Island manager error: {e}")
finally:
self.running = False
async def stop(self):
"""Stop island manager"""
self.running = False
logger.info("Stopping island manager")
def join_island(self, island_id: str, island_name: str, chain_id: str, is_hub: bool = False) -> bool:
"""Join an island"""
if island_id in self.islands:
logger.warning(f"Already member of island {island_id}")
return False
self.islands[island_id] = IslandMembership(
island_id=island_id,
island_name=island_name,
chain_id=chain_id,
status=IslandStatus.ACTIVE,
joined_at=time.time(),
is_hub=is_hub
)
self.island_peers[island_id] = set()
logger.info(f"Joined island {island_id} (name: {island_name}, chain: {chain_id})")
return True
def leave_island(self, island_id: str) -> bool:
"""Leave an island"""
if island_id == self.default_island_id:
logger.warning("Cannot leave default island")
return False
if island_id not in self.islands:
logger.warning(f"Not member of island {island_id}")
return False
# Remove from active bridges if present
if island_id in self.active_bridges:
self.active_bridges.remove(island_id)
del self.islands[island_id]
if island_id in self.island_peers:
del self.island_peers[island_id]
logger.info(f"Left island {island_id}")
return True
def request_bridge(self, target_island_id: str) -> str:
"""Request bridge to another island"""
if target_island_id in self.islands:
logger.warning(f"Already member of island {target_island_id}")
return ""
request_id = str(uuid.uuid4())
request = BridgeRequest(
request_id=request_id,
source_island_id=self.default_island_id,
target_island_id=target_island_id,
source_node_id=self.local_node_id,
timestamp=time.time(),
status="pending"
)
self.bridge_requests[request_id] = request
logger.info(f"Requested bridge to island {target_island_id} (request_id: {request_id})")
return request_id
def approve_bridge_request(self, request_id: str) -> bool:
"""Approve a bridge request"""
if request_id not in self.bridge_requests:
logger.warning(f"Unknown bridge request {request_id}")
return False
request = self.bridge_requests[request_id]
request.status = "approved"
# Add target island to our membership
self.join_island(request.target_island_id, f"bridge-{request.target_island_id[:8]}", f"bridge-{request.target_island_id[:8]}", is_hub=False)
# Mark as active bridge
self.active_bridges.add(request.target_island_id)
logger.info(f"Approved bridge request {request_id} to island {request.target_island_id}")
return True
def reject_bridge_request(self, request_id: str) -> bool:
"""Reject a bridge request"""
if request_id not in self.bridge_requests:
logger.warning(f"Unknown bridge request {request_id}")
return False
request = self.bridge_requests[request_id]
request.status = "rejected"
logger.info(f"Rejected bridge request {request_id} from island {request.source_island_id}")
return True
def get_island_peers(self, island_id: str) -> Set[str]:
"""Get peers in a specific island"""
return self.island_peers.get(island_id, set()).copy()
def add_island_peer(self, island_id: str, node_id: str):
"""Add a peer to an island"""
if island_id not in self.island_peers:
self.island_peers[island_id] = set()
self.island_peers[island_id].add(node_id)
# Update peer count
if island_id in self.islands:
self.islands[island_id].peer_count = len(self.island_peers[island_id])
def remove_island_peer(self, island_id: str, node_id: str):
"""Remove a peer from an island"""
if island_id in self.island_peers:
self.island_peers[island_id].discard(node_id)
# Update peer count
if island_id in self.islands:
self.islands[island_id].peer_count = len(self.island_peers[island_id])
def get_island_info(self, island_id: str) -> Optional[IslandMembership]:
"""Get information about an island"""
return self.islands.get(island_id)
def get_all_islands(self) -> List[IslandMembership]:
"""Get all island memberships"""
return list(self.islands.values())
def get_active_bridges(self) -> List[str]:
"""Get list of active bridge island IDs"""
return list(self.active_bridges)
def get_bridge_requests(self) -> List[BridgeRequest]:
"""Get all bridge requests"""
return list(self.bridge_requests.values())
def is_member_of_island(self, island_id: str) -> bool:
"""Check if node is member of an island"""
return island_id in self.islands
def is_bridged_to_island(self, island_id: str) -> bool:
"""Check if node has active bridge to an island"""
return island_id in self.active_bridges
async def _bridge_request_monitor(self):
"""Monitor bridge requests and handle timeouts"""
while self.running:
try:
current_time = time.time()
# Remove expired requests (older than 1 hour)
expired_requests = [
req_id for req_id, req in self.bridge_requests.items()
if current_time - req.timestamp > 3600 and req.status == "pending"
]
for req_id in expired_requests:
del self.bridge_requests[req_id]
logger.info(f"Removed expired bridge request {req_id}")
await asyncio.sleep(60) # Check every minute
except Exception as e:
logger.error(f"Bridge request monitor error: {e}")
await asyncio.sleep(10)
async def _island_health_check(self):
"""Check health of island memberships"""
while self.running:
try:
# Check for inactive islands (no peers for 10 minutes)
current_time = time.time()
for island_id, membership in list(self.islands.items()):
if island_id == self.default_island_id:
continue # Don't deactivate default island
peer_count = len(self.island_peers.get(island_id, set()))
if peer_count == 0 and membership.status == IslandStatus.ACTIVE:
# Check how long it's been inactive
if current_time - membership.joined_at > 600: # 10 minutes
membership.status = IslandStatus.INACTIVE
logger.warning(f"Island {island_id} marked as inactive (no peers)")
await asyncio.sleep(30) # Check every 30 seconds
except Exception as e:
logger.error(f"Island health check error: {e}")
await asyncio.sleep(10)
# Global island manager instance
island_manager_instance: Optional[IslandManager] = None
def get_island_manager() -> Optional[IslandManager]:
"""Get global island manager instance"""
return island_manager_instance
def create_island_manager(node_id: str, default_island_id: str, default_chain_id: str) -> IslandManager:
"""Create and set global island manager instance"""
global island_manager_instance
island_manager_instance = IslandManager(node_id, default_island_id, default_chain_id)
return island_manager_instance