//! Storage Network - P2P layer for storage nodes //! //! Handles peer discovery, data transfer, and network coordination. //! Built on libp2p when the 'node' feature is enabled. use crate::cid::ContentId; use crate::error::{Error, Result}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; /// Peer information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PeerInfo { /// Peer ID (derived from public key) pub peer_id: [u8; 32], /// Multiaddresses for this peer pub addresses: Vec, /// Regions served pub regions: Vec, /// Available capacity pub available_capacity: u64, /// Last seen timestamp pub last_seen: u64, /// Reputation score (0-100) pub reputation: u8, } /// Network message types #[derive(Debug, Clone, Serialize, Deserialize)] pub enum NetworkMessage { /// Request to retrieve a shard GetShard { deal_id: [u8; 32], shard_index: u8, }, /// Response with shard data ShardResponse { deal_id: [u8; 32], shard_index: u8, #[serde(with = "serde_bytes")] data: Vec, }, /// Announce storage availability AnnounceCapacity { available: u64, price_per_byte: u64, regions: Vec, }, /// Request to find providers for a CID FindProviders { cid: ContentId, }, /// Response with provider list ProvidersFound { cid: ContentId, providers: Vec<[u8; 32]>, }, /// Gossip about a new deal NewDeal { deal_id: [u8; 32], cid: ContentId, size: u64, }, } /// Storage network abstraction pub struct StorageNetwork { /// Our peer ID local_peer_id: [u8; 32], /// Known peers peers: HashMap<[u8; 32], PeerInfo>, /// CID to providers mapping (content routing) providers: HashMap>, /// Network state state: NetworkState, } /// Network state #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum NetworkState { /// Not connected Disconnected, /// Connecting to bootstrap nodes Bootstrapping, /// Fully connected Connected, } impl StorageNetwork { /// Create a new storage network pub fn new(local_peer_id: [u8; 32]) -> Self { Self { local_peer_id, peers: HashMap::new(), providers: HashMap::new(), state: NetworkState::Disconnected, } } /// Start the network #[cfg(feature = "node")] pub async fn start(&mut self, listen_addrs: &[String], bootstrap: &[String]) -> Result<()> { self.state = NetworkState::Bootstrapping; // TODO: Initialize libp2p swarm // - Create noise transport for encryption // - Create yamux multiplexer // - Set up Kademlia DHT for peer discovery // - Set up gossipsub for deal announcements // - Connect to bootstrap nodes self.state = NetworkState::Connected; Ok(()) } /// Start the network (stub for non-node builds) #[cfg(not(feature = "node"))] pub async fn start(&mut self, _listen_addrs: &[String], _bootstrap: &[String]) -> Result<()> { self.state = NetworkState::Connected; Ok(()) } /// Stop the network pub async fn stop(&mut self) { self.state = NetworkState::Disconnected; } /// Get network state pub fn state(&self) -> NetworkState { self.state } /// Add a known peer pub fn add_peer(&mut self, info: PeerInfo) { self.peers.insert(info.peer_id, info); } /// Remove a peer pub fn remove_peer(&mut self, peer_id: &[u8; 32]) { self.peers.remove(peer_id); } /// Get peer info pub fn get_peer(&self, peer_id: &[u8; 32]) -> Option<&PeerInfo> { self.peers.get(peer_id) } /// Get all connected peers pub fn connected_peers(&self) -> Vec<&PeerInfo> { self.peers.values().collect() } /// Find providers for a CID pub async fn find_providers(&self, cid: &ContentId) -> Vec<[u8; 32]> { self.providers.get(cid).cloned().unwrap_or_default() } /// Announce that we provide a CID pub async fn announce_provider(&mut self, cid: ContentId) { let providers = self.providers.entry(cid).or_default(); if !providers.contains(&self.local_peer_id) { providers.push(self.local_peer_id); } } /// Request a shard from a peer pub async fn request_shard( &self, peer_id: &[u8; 32], deal_id: [u8; 32], shard_index: u8, ) -> Result> { let _peer = self.peers.get(peer_id).ok_or_else(|| { Error::Network(format!("Peer not found: {:?}", hex::encode(peer_id))) })?; // TODO: Send GetShard message via libp2p // For now, return error as network not fully implemented Err(Error::Network("Network request not implemented".to_string())) } /// Broadcast a deal announcement pub async fn announce_deal(&self, deal_id: [u8; 32], cid: ContentId, size: u64) -> Result<()> { let _msg = NetworkMessage::NewDeal { deal_id, cid, size }; // TODO: Broadcast via gossipsub Ok(()) } /// Update reputation for a peer pub fn update_reputation(&mut self, peer_id: &[u8; 32], delta: i8) { if let Some(peer) = self.peers.get_mut(peer_id) { peer.reputation = peer.reputation.saturating_add_signed(delta); } } } /// Content routing - find where data is stored pub struct ContentRouter { /// Local provider records records: HashMap>, } impl ContentRouter { /// Create a new content router pub fn new() -> Self { Self { records: HashMap::new(), } } /// Add a provider record pub fn add_provider(&mut self, cid: ContentId, provider: [u8; 32]) { let providers = self.records.entry(cid).or_default(); if !providers.contains(&provider) { providers.push(provider); } } /// Remove a provider record pub fn remove_provider(&mut self, cid: &ContentId, provider: &[u8; 32]) { if let Some(providers) = self.records.get_mut(cid) { providers.retain(|p| p != provider); } } /// Find providers for a CID pub fn find_providers(&self, cid: &ContentId) -> &[[u8; 32]] { self.records.get(cid).map(|v| v.as_slice()).unwrap_or(&[]) } } impl Default for ContentRouter { fn default() -> Self { Self::new() } }