1use crate::PeerId;
20use crate::adaptive::trust::{TrustRecord, TrustSnapshot};
21use crate::adaptive::{AdaptiveDHT, AdaptiveDhtConfig, TrustEngine, TrustEvent};
22use crate::bootstrap::cache::{CachedCloseGroupPeer, CloseGroupCache};
23use crate::dht::core_engine::AddressType;
24use crate::dht_network_manager::{
25 DhtNetworkConfig, DhtNetworkEvent, DhtNetworkManager, IDENTITY_EXCHANGE_TIMEOUT,
26};
27use crate::error::{IdentityError, NetworkError, P2PError, P2pResult as Result};
28use crate::reachability::spawn_acquisition_driver;
29
30use crate::MultiAddr;
31use crate::identity::node_identity::{NodeIdentity, peer_id_from_public_key};
32use crate::quantum_crypto::saorsa_transport_integration::{MlDsaPublicKey, MlDsaSignature};
33use dashmap::DashMap;
34use futures::StreamExt;
35use parking_lot::Mutex as ParkingMutex;
36use serde::{Deserialize, Serialize};
37use std::collections::HashMap;
38use std::net::SocketAddr;
39use std::path::{Path, PathBuf};
40use std::sync::Arc;
41use std::sync::atomic::{AtomicBool, Ordering};
42use std::time::{Duration, SystemTime, UNIX_EPOCH};
43use tokio::sync::{Mutex as TokioMutex, RwLock, broadcast};
44use tokio::time::Instant;
45use tokio_util::sync::CancellationToken;
46use tracing::{debug, info, trace, warn};
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
52pub(crate) struct WireMessage {
53 pub(crate) protocol: String,
55 pub(crate) data: Vec<u8>,
57 pub(crate) from: PeerId,
59 pub(crate) timestamp: u64,
61 #[serde(default)]
67 pub(crate) user_agent: String,
68 #[serde(default)]
70 pub(crate) public_key: Vec<u8>,
71 #[serde(default)]
73 pub(crate) signature: Vec<u8>,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
82pub enum NodeMode {
83 #[default]
85 Node,
86 Client,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92enum ListenMode {
93 Public,
95 Local,
97}
98
99pub fn user_agent_for_mode(mode: NodeMode) -> String {
104 let prefix = match mode {
105 NodeMode::Node => "node",
106 NodeMode::Client => "client",
107 };
108 format!("{prefix}/{}", env!("CARGO_PKG_VERSION"))
109}
110
111pub fn is_dht_participant(user_agent: &str) -> bool {
113 user_agent.starts_with("node/")
114}
115
116pub(crate) const MESSAGE_RECV_CHANNEL_CAPACITY: usize = 256;
118
119pub(crate) const MAX_ACTIVE_REQUESTS: usize = 256;
121
122pub(crate) const MAX_REQUEST_TIMEOUT: Duration = Duration::from_secs(300);
124
125const DEFAULT_LISTEN_PORT: u16 = 9000;
127
128const DEFAULT_MAX_CONNECTIONS: usize = 10_000;
130
131const DEFAULT_CONNECTION_TIMEOUT_SECS: u64 = 25;
138
139const BOOTSTRAP_IDENTITY_TIMEOUT_SECS: u64 = 3;
152
153const MAX_CONCURRENT_BOOTSTRAP_DIALS: usize = 4;
160
161const CLIENT_BOOTSTRAP_TARGET: usize = 6;
170
171const fn default_true() -> bool {
173 true
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct NodeConfig {
179 #[serde(default)]
185 pub local: bool,
186
187 #[serde(default)]
189 pub port: u16,
190
191 #[serde(default = "default_true")]
196 pub ipv6: bool,
197
198 pub bootstrap_peers: Vec<crate::MultiAddr>,
200
201 pub connection_timeout: Duration,
204
205 pub max_connections: usize,
207
208 pub dht_config: DHTConfig,
210
211 pub diversity_config: Option<crate::security::IPDiversityConfig>,
216
217 #[serde(default)]
221 pub max_message_size: Option<usize>,
222
223 #[serde(skip)]
228 pub node_identity: Option<Arc<NodeIdentity>>,
229
230 #[serde(default)]
236 pub mode: NodeMode,
237
238 #[serde(default, skip_serializing_if = "Option::is_none")]
243 pub custom_user_agent: Option<String>,
244
245 #[serde(default)]
253 pub allow_loopback: bool,
254
255 #[serde(default)]
263 pub adaptive_dht_config: AdaptiveDhtConfig,
264
265 #[serde(default, skip_serializing_if = "Option::is_none")]
276 pub close_group_cache_dir: Option<PathBuf>,
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct DHTConfig {
282 pub k_value: usize,
284
285 pub alpha_value: usize,
287
288 pub refresh_interval: Duration,
290}
291
292#[inline]
305fn build_listen_addrs(port: u16, ipv6_enabled: bool, mode: ListenMode) -> Vec<MultiAddr> {
306 let mut addrs = Vec::with_capacity(if ipv6_enabled { 2 } else { 1 });
307
308 let (v4, v6) = match mode {
309 ListenMode::Public => (
310 std::net::Ipv4Addr::UNSPECIFIED,
311 std::net::Ipv6Addr::UNSPECIFIED,
312 ),
313 ListenMode::Local => (std::net::Ipv4Addr::LOCALHOST, std::net::Ipv6Addr::LOCALHOST),
314 };
315
316 if ipv6_enabled {
317 addrs.push(MultiAddr::quic(std::net::SocketAddr::new(
318 std::net::IpAddr::V6(v6),
319 port,
320 )));
321 }
322
323 addrs.push(MultiAddr::quic(std::net::SocketAddr::new(
324 std::net::IpAddr::V4(v4),
325 port,
326 )));
327
328 addrs
329}
330
331impl NodeConfig {
332 pub fn user_agent(&self) -> String {
337 self.custom_user_agent
338 .clone()
339 .unwrap_or_else(|| user_agent_for_mode(self.mode))
340 }
341
342 pub fn listen_addrs(&self) -> Vec<MultiAddr> {
347 let mode = if self.local {
348 ListenMode::Local
349 } else {
350 ListenMode::Public
351 };
352 build_listen_addrs(self.port, self.ipv6, mode)
353 }
354
355 pub fn new() -> Result<Self> {
361 Ok(Self::default())
362 }
363
364 pub fn builder() -> NodeConfigBuilder {
366 NodeConfigBuilder::default()
367 }
368}
369
370#[derive(Debug, Clone)]
393pub struct NodeConfigBuilder {
394 port: u16,
395 ipv6: bool,
396 local: bool,
397 bootstrap_peers: Vec<crate::MultiAddr>,
398 max_connections: Option<usize>,
399 connection_timeout: Option<Duration>,
400 dht_config: Option<DHTConfig>,
401 max_message_size: Option<usize>,
402 mode: NodeMode,
403 custom_user_agent: Option<String>,
404 allow_loopback: Option<bool>,
405 adaptive_dht_config: Option<AdaptiveDhtConfig>,
406 close_group_cache_dir: Option<PathBuf>,
407}
408
409impl Default for NodeConfigBuilder {
410 fn default() -> Self {
411 Self {
412 port: 0,
413 ipv6: true,
414 local: false,
415 bootstrap_peers: Vec::new(),
416 max_connections: None,
417 connection_timeout: None,
418 dht_config: None,
419 max_message_size: None,
420 mode: NodeMode::default(),
421 custom_user_agent: None,
422 allow_loopback: None,
423 adaptive_dht_config: None,
424 close_group_cache_dir: None,
425 }
426 }
427}
428
429impl NodeConfigBuilder {
430 pub fn port(mut self, port: u16) -> Self {
432 self.port = port;
433 self
434 }
435
436 pub fn ipv6(mut self, enabled: bool) -> Self {
438 self.ipv6 = enabled;
439 self
440 }
441
442 pub fn local(mut self, local: bool) -> Self {
449 self.local = local;
450 self
451 }
452
453 pub fn bootstrap_peer(mut self, addr: crate::MultiAddr) -> Self {
455 self.bootstrap_peers.push(addr);
456 self
457 }
458
459 pub fn max_connections(mut self, max: usize) -> Self {
461 self.max_connections = Some(max);
462 self
463 }
464
465 pub fn connection_timeout(mut self, timeout: Duration) -> Self {
467 self.connection_timeout = Some(timeout);
468 self
469 }
470
471 pub fn dht_config(mut self, config: DHTConfig) -> Self {
473 self.dht_config = Some(config);
474 self
475 }
476
477 pub fn max_message_size(mut self, max_message_size: usize) -> Self {
481 self.max_message_size = Some(max_message_size);
482 self
483 }
484
485 pub fn mode(mut self, mode: NodeMode) -> Self {
487 self.mode = mode;
488 self
489 }
490
491 pub fn custom_user_agent(mut self, user_agent: impl Into<String>) -> Self {
493 self.custom_user_agent = Some(user_agent.into());
494 self
495 }
496
497 pub fn allow_loopback(mut self, allow: bool) -> Self {
501 self.allow_loopback = Some(allow);
502 self
503 }
504
505 pub fn trust_enforcement(mut self, enabled: bool) -> Self {
518 let threshold = if enabled {
519 AdaptiveDhtConfig::default().swap_threshold
520 } else {
521 0.0
522 };
523 self.adaptive_dht_config = Some(AdaptiveDhtConfig {
524 swap_threshold: threshold,
525 });
526 self
527 }
528
529 pub fn adaptive_dht_config(mut self, config: AdaptiveDhtConfig) -> Self {
533 self.adaptive_dht_config = Some(config);
534 self
535 }
536
537 pub fn close_group_cache_dir(mut self, path: impl Into<PathBuf>) -> Self {
542 self.close_group_cache_dir = Some(path.into());
543 self
544 }
545
546 pub fn build(self) -> Result<NodeConfig> {
552 let allow_loopback = self.allow_loopback.unwrap_or(self.local);
554
555 Ok(NodeConfig {
556 local: self.local,
557 port: self.port,
558 ipv6: self.ipv6,
559 bootstrap_peers: self.bootstrap_peers,
560 connection_timeout: self
561 .connection_timeout
562 .unwrap_or(Duration::from_secs(DEFAULT_CONNECTION_TIMEOUT_SECS)),
563 max_connections: self.max_connections.unwrap_or(DEFAULT_MAX_CONNECTIONS),
564 dht_config: self.dht_config.unwrap_or_default(),
565 diversity_config: None,
566 max_message_size: self.max_message_size,
567 node_identity: None,
568 mode: self.mode,
569 custom_user_agent: self.custom_user_agent,
570 allow_loopback,
571 adaptive_dht_config: self.adaptive_dht_config.unwrap_or_default(),
572 close_group_cache_dir: self.close_group_cache_dir,
573 })
574 }
575}
576
577impl Default for NodeConfig {
578 fn default() -> Self {
579 Self {
580 local: false,
581 port: DEFAULT_LISTEN_PORT,
582 ipv6: true,
583 bootstrap_peers: Vec::new(),
584 connection_timeout: Duration::from_secs(DEFAULT_CONNECTION_TIMEOUT_SECS),
585 max_connections: DEFAULT_MAX_CONNECTIONS,
586 dht_config: DHTConfig::default(),
587 diversity_config: None,
588 max_message_size: None,
589 node_identity: None,
590 mode: NodeMode::default(),
591 custom_user_agent: None,
592 allow_loopback: false,
593 adaptive_dht_config: AdaptiveDhtConfig::default(),
594 close_group_cache_dir: None,
595 }
596 }
597}
598
599impl DHTConfig {
600 pub const DEFAULT_K_VALUE: usize = 20;
602 const DEFAULT_ALPHA_VALUE: usize = 3;
603 const DEFAULT_REFRESH_INTERVAL_SECS: u64 = 600;
604 const MIN_K_VALUE: usize = 4;
606
607 pub fn validate(&self) -> Result<()> {
611 if self.k_value < Self::MIN_K_VALUE {
612 return Err(P2PError::Validation(
613 format!(
614 "k_value must be >= {} (got {}), values below {} produce degenerate behavior",
615 Self::MIN_K_VALUE,
616 self.k_value,
617 Self::MIN_K_VALUE,
618 )
619 .into(),
620 ));
621 }
622 if self.alpha_value < 1 {
623 return Err(P2PError::Validation(
624 format!("alpha_value must be >= 1 (got {})", self.alpha_value).into(),
625 ));
626 }
627 if self.refresh_interval.is_zero() {
628 return Err(P2PError::Validation("refresh_interval must be > 0".into()));
629 }
630 Ok(())
631 }
632}
633
634impl Default for DHTConfig {
635 fn default() -> Self {
636 Self {
637 k_value: Self::DEFAULT_K_VALUE,
638 alpha_value: Self::DEFAULT_ALPHA_VALUE,
639 refresh_interval: Duration::from_secs(Self::DEFAULT_REFRESH_INTERVAL_SECS),
640 }
641 }
642}
643
644#[derive(Debug, Clone)]
646pub struct PeerInfo {
647 #[allow(dead_code)]
649 pub(crate) channel_id: String,
650
651 pub addresses: Vec<MultiAddr>,
653
654 pub connected_at: Instant,
656
657 pub last_seen: Instant,
659
660 pub status: ConnectionStatus,
662
663 pub protocols: Vec<String>,
665
666 pub heartbeat_count: u64,
668}
669
670#[derive(Debug, Clone, PartialEq)]
672pub enum ConnectionStatus {
673 Connecting,
675 Connected,
677 Disconnecting,
679 Disconnected,
681 Failed(String),
683}
684
685#[derive(Debug, Clone)]
690pub enum P2PEvent {
691 Message {
693 topic: String,
695 source: Option<PeerId>,
698 transport_source: Option<MultiAddr>,
702 timestamp: u64,
710 data: Vec<u8>,
712 },
713 PeerConnected(PeerId, String),
716 PeerDisconnected(PeerId),
718}
719
720#[derive(Debug, Clone)]
725pub struct PeerResponse {
726 pub peer_id: PeerId,
728 pub data: Vec<u8>,
730 pub latency: Duration,
732}
733
734#[derive(Debug, Clone, Serialize, Deserialize)]
739pub(crate) struct RequestResponseEnvelope {
740 pub(crate) message_id: String,
742 pub(crate) is_response: bool,
744 pub(crate) payload: Vec<u8>,
746}
747
748pub(crate) struct PendingRequest {
750 pub(crate) response_tx: tokio::sync::oneshot::Sender<Vec<u8>>,
752 pub(crate) expected_peer: PeerId,
754}
755
756const QUIC_TEARDOWN_GRACE: Duration = Duration::from_millis(100);
762
763pub struct P2PNode {
774 config: NodeConfig,
776
777 peer_id: PeerId,
779
780 transport: Arc<crate::transport_handle::TransportHandle>,
782
783 start_time: Instant,
785
786 shutdown: CancellationToken,
788
789 adaptive_dht: AdaptiveDHT,
792
793 is_bootstrapped: Arc<AtomicBool>,
795
796 is_started: Arc<AtomicBool>,
798
799 reconnect_locks: ParkingMutex<HashMap<PeerId, Arc<TokioMutex<()>>>>,
803
804 relayer_peer_id: Arc<RwLock<Option<PeerId>>>,
813
814 relay_address: Arc<RwLock<Option<SocketAddr>>>,
821}
822
823pub(crate) fn normalize_wildcard_to_loopback(addr: std::net::SocketAddr) -> std::net::SocketAddr {
839 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
840
841 if addr.ip().is_unspecified() {
842 let loopback_ip = match addr {
844 std::net::SocketAddr::V6(_) => IpAddr::V6(Ipv6Addr::LOCALHOST), std::net::SocketAddr::V4(_) => IpAddr::V4(Ipv4Addr::LOCALHOST), };
847 std::net::SocketAddr::new(loopback_ip, addr.port())
848 } else {
849 addr
851 }
852}
853
854impl P2PNode {
855 pub async fn new(config: NodeConfig) -> Result<Self> {
857 let node_identity = match config.node_identity.clone() {
859 Some(identity) => identity,
860 None => Arc::new(NodeIdentity::generate()?),
861 };
862
863 let peer_id = *node_identity.peer_id();
865
866 config.dht_config.validate()?;
869 if let Some(ref diversity) = config.diversity_config {
870 diversity
871 .validate()
872 .map_err(|e| P2PError::Validation(format!("IP diversity config: {e}").into()))?;
873 }
874
875 let transport_config = crate::transport_handle::TransportConfig::from_node_config(
877 &config,
878 crate::DEFAULT_EVENT_CHANNEL_CAPACITY,
879 node_identity.clone(),
880 );
881 let transport =
882 Arc::new(crate::transport_handle::TransportHandle::new(transport_config).await?);
883
884 let dht_manager_config = DhtNetworkConfig {
886 peer_id,
887 node_config: config.clone(),
888 request_timeout: config.connection_timeout,
889 max_concurrent_operations: MAX_ACTIVE_REQUESTS,
890 enable_security: true,
891 swap_threshold: 0.0, };
893 let adaptive_dht = AdaptiveDHT::new(
894 transport.clone(),
895 dht_manager_config,
896 config.adaptive_dht_config.clone(),
897 )
898 .await?;
899
900 let node = Self {
901 config,
902 peer_id,
903 transport,
904 start_time: Instant::now(),
905 shutdown: CancellationToken::new(),
906 adaptive_dht,
907 is_bootstrapped: Arc::new(AtomicBool::new(false)),
908 is_started: Arc::new(AtomicBool::new(false)),
909 reconnect_locks: ParkingMutex::new(HashMap::new()),
910 relayer_peer_id: Arc::new(RwLock::new(None)),
911 relay_address: Arc::new(RwLock::new(None)),
912 };
913 info!(
914 "Created P2P node with peer ID: {} (call start() to begin networking)",
915 node.peer_id
916 );
917
918 Ok(node)
919 }
920
921 pub fn peer_id(&self) -> &PeerId {
923 &self.peer_id
924 }
925
926 pub fn transport(&self) -> &Arc<crate::transport_handle::TransportHandle> {
928 &self.transport
929 }
930
931 pub async fn relay_address(&self) -> Option<SocketAddr> {
938 *self.relay_address.read().await
939 }
940
941 pub fn local_addr(&self) -> Option<MultiAddr> {
942 self.transport.local_addr()
943 }
944
945 pub fn is_bootstrapped(&self) -> bool {
950 self.is_bootstrapped.load(Ordering::SeqCst)
951 }
952
953 pub async fn re_bootstrap(&self) -> Result<()> {
958 self.is_bootstrapped.store(false, Ordering::SeqCst);
959 self.connect_bootstrap_peers(None).await
960 }
961
962 pub fn trust_engine(&self) -> Arc<TrustEngine> {
968 self.adaptive_dht.trust_engine().clone()
969 }
970
971 pub async fn report_trust_event(&self, peer_id: &PeerId, event: TrustEvent) {
985 self.adaptive_dht.report_trust_event(peer_id, event).await;
986 }
987
988 pub fn peer_trust(&self, peer_id: &PeerId) -> f64 {
992 self.adaptive_dht.peer_trust(peer_id)
993 }
994
995 pub fn adaptive_dht(&self) -> &AdaptiveDHT {
997 &self.adaptive_dht
998 }
999
1000 pub async fn send_request(
1033 &self,
1034 peer_id: &PeerId,
1035 protocol: &str,
1036 data: Vec<u8>,
1037 timeout: Duration,
1038 ) -> Result<PeerResponse> {
1039 let result = self
1040 .send_request_reconnecting(peer_id, protocol, data, timeout)
1041 .await;
1042 if let Err(ref e) = result {
1043 let event = if matches!(e, P2PError::Timeout(_)) {
1044 TrustEvent::ConnectionTimeout
1045 } else {
1046 TrustEvent::ConnectionFailed
1047 };
1048 self.report_trust_event(peer_id, event).await;
1049 }
1050 result
1051 }
1052
1053 async fn send_request_reconnecting(
1070 &self,
1071 peer_id: &PeerId,
1072 protocol: &str,
1073 data: Vec<u8>,
1074 timeout: Duration,
1075 ) -> Result<PeerResponse> {
1076 let existing_channels = self.transport.channels_for_peer(peer_id).await;
1081
1082 if existing_channels.is_empty() {
1085 {
1090 let lock = self.reconnect_lock_for(peer_id);
1091 let _guard = lock.lock().await;
1092 if !self.transport.is_peer_connected(peer_id).await {
1094 self.ensure_channel(peer_id, &[], &[], &[]).await?;
1095 }
1096 }
1097 return self
1098 .transport
1099 .send_request(peer_id, protocol, data, timeout)
1100 .await;
1101 }
1102
1103 let saved_addrs: Vec<MultiAddr> = self
1106 .transport
1107 .peer_info(peer_id)
1108 .await
1109 .map(|info| info.addresses)
1110 .unwrap_or_default();
1111
1112 let retry_data = data.clone();
1115
1116 match self
1118 .transport
1119 .send_request(peer_id, protocol, data, timeout)
1120 .await
1121 {
1122 Ok(resp) => return Ok(resp),
1123 Err(e) => {
1124 if !e.is_stale_channel_send_failure() {
1128 return Err(e);
1129 }
1130 debug!(
1131 peer = %peer_id.to_hex(),
1132 error = %e,
1133 "stale channel request failed, attempting reconnect",
1134 );
1135 }
1136 }
1137
1138 {
1143 let lock = self.reconnect_lock_for(peer_id);
1144 let _guard = lock.lock().await;
1145
1146 if self.transport.is_peer_connected(peer_id).await {
1148 for channel_id in &existing_channels {
1151 self.transport.disconnect_channel(channel_id).await;
1152 }
1153 } else {
1154 self.ensure_channel(peer_id, &[], &saved_addrs, &existing_channels)
1155 .await?;
1156 }
1157 }
1158 self.transport
1159 .send_request(peer_id, protocol, retry_data, timeout)
1160 .await
1161 }
1162
1163 pub async fn send_response(
1164 &self,
1165 peer_id: &PeerId,
1166 protocol: &str,
1167 message_id: &str,
1168 data: Vec<u8>,
1169 ) -> Result<()> {
1170 self.transport
1171 .send_response(peer_id, protocol, message_id, data)
1172 .await
1173 }
1174
1175 pub fn parse_request_envelope(data: &[u8]) -> Option<(String, bool, Vec<u8>)> {
1176 crate::transport_handle::TransportHandle::parse_request_envelope(data)
1177 }
1178
1179 pub async fn subscribe(&self, topic: &str) -> Result<()> {
1180 self.transport.subscribe(topic).await
1181 }
1182
1183 pub async fn publish(&self, topic: &str, data: &[u8]) -> Result<()> {
1184 self.transport.publish(topic, data).await
1185 }
1186
1187 pub fn config(&self) -> &NodeConfig {
1189 &self.config
1190 }
1191
1192 pub async fn start(&self) -> Result<()> {
1194 info!("Starting P2P node...");
1195
1196 self.transport.start_network_listeners().await?;
1198
1199 self.adaptive_dht.start().await?;
1201
1202 let listen_addrs = self.transport.listen_addrs().await;
1204 info!("P2P node started on addresses: {:?}", listen_addrs);
1205
1206 let close_group_cache = if let Some(ref dir) = self.config.close_group_cache_dir {
1213 match CloseGroupCache::load_from_dir(dir).await {
1214 Ok(Some(cache)) => {
1215 let original_count = cache.peers.len();
1218 let cache = CloseGroupCache {
1219 peers: cache
1220 .peers
1221 .into_iter()
1222 .filter(|p| p.trust.score.is_finite())
1223 .collect(),
1224 ..cache
1225 };
1226 let filtered_count = original_count - cache.peers.len();
1227 if filtered_count > 0 {
1228 warn!(
1229 "Filtered {filtered_count} peers with non-finite trust scores from close group cache"
1230 );
1231 }
1232
1233 let trust_snapshot = TrustSnapshot {
1234 peers: cache
1235 .peers
1236 .iter()
1237 .map(|p| (p.peer_id, p.trust.clone()))
1238 .collect(),
1239 };
1240 self.adaptive_dht
1241 .trust_engine()
1242 .import_snapshot(&trust_snapshot);
1243 info!(
1244 "Loaded {} peers from close group cache (trust scores imported)",
1245 cache.peers.len()
1246 );
1247 Some(cache)
1248 }
1249 Ok(None) => {
1250 debug!(
1251 "No close group cache found in {}, fresh start",
1252 dir.display()
1253 );
1254 None
1255 }
1256 Err(e) => {
1257 warn!(
1258 "Failed to load close group cache from {}: {e}",
1259 dir.display()
1260 );
1261 None
1262 }
1263 }
1264 } else {
1265 None
1266 };
1267
1268 self.connect_bootstrap_peers(close_group_cache.as_ref())
1270 .await?;
1271
1272 {
1279 let dht = self.adaptive_dht.dht_manager();
1280 let rt_size = dht.get_routing_table_size().await;
1281 dht.emit_event(DhtNetworkEvent::BootstrapComplete { num_peers: rt_size });
1282 }
1283
1284 if self.config.mode != NodeMode::Client {
1298 spawn_acquisition_driver(
1299 self.adaptive_dht.dht_manager().clone(),
1300 Arc::clone(&self.transport),
1301 Arc::clone(&self.relayer_peer_id),
1302 Arc::clone(&self.relay_address),
1303 self.shutdown.clone(),
1304 );
1305 } else {
1306 info!("client mode — skipping relay acquisition driver");
1307 }
1308
1309 {
1350 let transport = Arc::clone(&self.transport);
1351 let dht = self.adaptive_dht.dht_manager().clone();
1352 let shutdown = self.shutdown.clone();
1353 tokio::spawn(async move {
1354 loop {
1355 tokio::select! {
1356 biased;
1357 _ = shutdown.cancelled() => break,
1358 update = transport.recv_peer_address_update() => {
1359 let Some((peer_addr, advertised_addr)) = update else { break };
1360 let normalized_peer =
1361 saorsa_transport::shared::normalize_socket_addr(peer_addr);
1362 let normalized_adv =
1363 saorsa_transport::shared::normalize_socket_addr(advertised_addr);
1364 if normalized_peer.ip() == normalized_adv.ip() {
1369 debug!(
1370 "DHT_BRIDGE: dropping same-IP update peer={} addr={}",
1371 normalized_peer,
1372 normalized_adv
1373 );
1374 continue;
1375 }
1376 info!(
1377 "DHT_BRIDGE: processing relay update peer={} addr={}",
1378 normalized_peer,
1379 normalized_adv
1380 );
1381 if let Some(peer_id) = transport.peer_id_for_addr(&normalized_peer).await {
1386 let multi_addr = MultiAddr::quic(normalized_adv);
1387 info!(
1388 "Updating DHT: peer {} relay address {} (connection was {})",
1389 peer_id, advertised_addr, peer_addr
1390 );
1391 if !dht
1392 .touch_legacy_relay_hint_if_unsequenced(&peer_id, &multi_addr)
1393 .await
1394 {
1395 debug!(
1396 "DHT_BRIDGE: ignored legacy relay hint for sequenced peer {} addr {}",
1397 peer_id, advertised_addr
1398 );
1399 }
1400 }
1401 }
1402 }
1403 }
1404 });
1405 }
1406
1407 self.is_started
1408 .store(true, std::sync::atomic::Ordering::Release);
1409
1410 Ok(())
1411 }
1412
1413 pub async fn run(&self) -> Result<()> {
1418 if !self.is_running() {
1419 self.start().await?;
1420 }
1421
1422 info!("P2P node running...");
1423
1424 self.shutdown.cancelled().await;
1427
1428 info!("P2P node stopped");
1429 Ok(())
1430 }
1431
1432 pub async fn stop(&self) -> Result<()> {
1434 info!("Stopping P2P node...");
1435
1436 if let Some(ref dir) = self.config.close_group_cache_dir
1438 && let Err(e) = self.save_close_group_cache(dir).await
1439 {
1440 warn!("Failed to save close group cache on shutdown: {e}");
1441 }
1442
1443 self.shutdown.cancel();
1445
1446 self.adaptive_dht.stop().await?;
1448
1449 self.transport.stop().await?;
1451
1452 self.is_started
1453 .store(false, std::sync::atomic::Ordering::Release);
1454
1455 info!("P2P node stopped");
1456 Ok(())
1457 }
1458
1459 pub async fn shutdown(&self) -> Result<()> {
1461 self.stop().await
1462 }
1463
1464 pub fn is_running(&self) -> bool {
1466 self.is_started.load(std::sync::atomic::Ordering::Acquire) && !self.shutdown.is_cancelled()
1467 }
1468
1469 pub async fn listen_addrs(&self) -> Vec<MultiAddr> {
1471 self.transport.listen_addrs().await
1472 }
1473
1474 pub async fn connected_peers(&self) -> Vec<PeerId> {
1476 self.transport.connected_peers().await
1477 }
1478
1479 pub async fn peer_count(&self) -> usize {
1481 self.transport.peer_count().await
1482 }
1483
1484 pub async fn peer_info(&self, peer_id: &PeerId) -> Option<PeerInfo> {
1486 self.transport.peer_info(peer_id).await
1487 }
1488
1489 #[allow(dead_code)]
1491 pub(crate) async fn get_channel_id_by_address(&self, addr: &MultiAddr) -> Option<String> {
1492 self.transport.get_channel_id_by_address(addr).await
1493 }
1494
1495 #[allow(dead_code)]
1497 pub(crate) async fn list_active_connections(&self) -> Vec<(String, Vec<MultiAddr>)> {
1498 self.transport.list_active_connections().await
1499 }
1500
1501 #[allow(dead_code)]
1503 pub(crate) async fn remove_channel(&self, channel_id: &str) -> bool {
1504 self.transport.remove_channel(channel_id).await
1505 }
1506
1507 pub(crate) async fn disconnect_channel(&self, channel_id: &str) {
1512 self.transport.disconnect_channel(channel_id).await;
1513 }
1514
1515 pub async fn is_peer_connected(&self, peer_id: &PeerId) -> bool {
1517 self.transport.is_peer_connected(peer_id).await
1518 }
1519
1520 pub async fn connect_peer(&self, address: &MultiAddr) -> Result<String> {
1531 self.transport.connect_peer(address).await
1532 }
1533
1534 pub async fn connect_peer_typed(
1541 &self,
1542 address: &MultiAddr,
1543 kind: AddressType,
1544 ) -> Result<String> {
1545 self.transport.connect_peer_typed(address, kind).await
1546 }
1547
1548 pub async fn wait_for_peer_identity(
1555 &self,
1556 channel_id: &str,
1557 timeout: Duration,
1558 ) -> Result<PeerId> {
1559 self.transport
1560 .wait_for_peer_identity(channel_id, timeout)
1561 .await
1562 }
1563
1564 pub async fn disconnect_peer(&self, peer_id: &PeerId) -> Result<()> {
1566 self.transport.disconnect_peer(peer_id).await
1567 }
1568
1569 #[allow(dead_code)]
1571 pub(crate) async fn is_connection_active(&self, channel_id: &str) -> bool {
1572 self.transport.is_connection_active(channel_id).await
1573 }
1574
1575 pub async fn send_message(
1589 &self,
1590 peer_id: &PeerId,
1591 protocol: &str,
1592 data: Vec<u8>,
1593 addrs: &[MultiAddr],
1594 ) -> Result<()> {
1595 let existing_channels = self.transport.channels_for_peer(peer_id).await;
1600
1601 if existing_channels.is_empty() {
1604 let lock = self.reconnect_lock_for(peer_id);
1605 let _guard = lock.lock().await;
1606
1607 if self.transport.is_peer_connected(peer_id).await {
1609 return self.transport.send_message(peer_id, protocol, data).await;
1610 }
1611
1612 return self
1613 .reconnect_and_send(peer_id, protocol, data, addrs, &[], &[])
1614 .await;
1615 }
1616
1617 let saved_addrs: Vec<MultiAddr> = self
1620 .transport
1621 .peer_info(peer_id)
1622 .await
1623 .map(|info| info.addresses)
1624 .unwrap_or_default();
1625
1626 let retry_data = data.clone();
1629
1630 let send_result = self.transport.send_message(peer_id, protocol, data).await;
1632 match send_result {
1633 Ok(()) => return Ok(()),
1634 Err(e) => {
1635 if !e.is_stale_channel_send_failure() {
1636 debug!(
1637 peer = %peer_id.to_hex(),
1638 error = %e,
1639 "send failed during active channel use, not reconnecting",
1640 );
1641 return Err(e);
1642 }
1643
1644 debug!(
1645 peer = %peer_id.to_hex(),
1646 error = %e,
1647 "stale channel send failed, attempting reconnect",
1648 );
1649 }
1650 }
1651
1652 let lock = self.reconnect_lock_for(peer_id);
1655 let _guard = lock.lock().await;
1656
1657 if self.transport.is_peer_connected(peer_id).await {
1659 for channel_id in &existing_channels {
1663 self.transport.disconnect_channel(channel_id).await;
1664 }
1665 return self
1666 .transport
1667 .send_message(peer_id, protocol, retry_data)
1668 .await;
1669 }
1670
1671 self.reconnect_and_send(
1672 peer_id,
1673 protocol,
1674 retry_data,
1675 addrs,
1676 &saved_addrs,
1677 &existing_channels,
1678 )
1679 .await
1680 }
1681
1682 async fn ensure_channel(
1697 &self,
1698 peer_id: &PeerId,
1699 addrs: &[MultiAddr],
1700 saved_addrs: &[MultiAddr],
1701 stale_channels: &[String],
1702 ) -> Result<()> {
1703 let (address, kind) = self
1705 .resolve_dial_address(peer_id, addrs, saved_addrs)
1706 .await
1707 .ok_or_else(|| {
1708 P2PError::Network(NetworkError::PeerNotFound(peer_id.to_hex().into()))
1709 })?;
1710
1711 if !stale_channels.is_empty() {
1717 for channel_id in stale_channels {
1718 self.transport.disconnect_channel(channel_id).await;
1719 }
1720 tokio::time::sleep(QUIC_TEARDOWN_GRACE).await;
1721 }
1722
1723 let channel_id = self.transport.connect_peer_typed(&address, kind).await?;
1725 let authenticated = match self
1726 .transport
1727 .wait_for_peer_identity(&channel_id, IDENTITY_EXCHANGE_TIMEOUT)
1728 .await
1729 {
1730 Ok(peer) => peer,
1731 Err(e) => {
1732 self.transport.disconnect_channel(&channel_id).await;
1735 return Err(e);
1736 }
1737 };
1738
1739 if &authenticated != peer_id {
1740 self.transport.disconnect_channel(&channel_id).await;
1741 return Err(P2PError::Identity(IdentityError::IdentityMismatch {
1742 expected: peer_id.to_hex().into(),
1743 actual: authenticated.to_hex().into(),
1744 }));
1745 }
1746
1747 Ok(())
1748 }
1749
1750 async fn reconnect_and_send(
1752 &self,
1753 peer_id: &PeerId,
1754 protocol: &str,
1755 data: Vec<u8>,
1756 addrs: &[MultiAddr],
1757 saved_addrs: &[MultiAddr],
1758 stale_channels: &[String],
1759 ) -> Result<()> {
1760 self.ensure_channel(peer_id, addrs, saved_addrs, stale_channels)
1761 .await?;
1762 self.transport.send_message(peer_id, protocol, data).await
1764 }
1765
1766 async fn resolve_dial_address(
1777 &self,
1778 peer_id: &PeerId,
1779 caller_addrs: &[MultiAddr],
1780 saved_addrs: &[MultiAddr],
1781 ) -> Option<(MultiAddr, AddressType)> {
1782 if let Some(addr) = Self::first_dialable(caller_addrs) {
1789 return Some((addr, AddressType::Unverified));
1790 }
1791 if let Some(addr) = Self::first_dialable(saved_addrs) {
1792 return Some((addr, AddressType::Unverified));
1793 }
1794
1795 self.adaptive_dht
1796 .peer_addresses_for_dial_typed(peer_id)
1797 .await
1798 .into_iter()
1799 .find(|(a, _)| {
1800 a.dialable_socket_addr()
1801 .is_some_and(|sa| !sa.ip().is_unspecified())
1802 })
1803 }
1804
1805 fn first_dialable(addrs: &[MultiAddr]) -> Option<MultiAddr> {
1808 addrs
1809 .iter()
1810 .find(|a| {
1811 let dialable = a
1812 .dialable_socket_addr()
1813 .is_some_and(|sa| !sa.ip().is_unspecified());
1814 if !dialable {
1815 trace!(address = %a, "skipping non-dialable address");
1816 }
1817 dialable
1818 })
1819 .cloned()
1820 }
1821
1822 fn reconnect_lock_for(&self, peer_id: &PeerId) -> Arc<TokioMutex<()>> {
1824 self.reconnect_locks
1825 .lock()
1826 .entry(*peer_id)
1827 .or_insert_with(|| Arc::new(TokioMutex::new(())))
1828 .clone()
1829 }
1830}
1831
1832fn protocol_error(msg: impl std::fmt::Display) -> P2PError {
1834 P2PError::Network(NetworkError::ProtocolError(msg.to_string().into()))
1835}
1836
1837pub(crate) fn broadcast_event(tx: &broadcast::Sender<P2PEvent>, event: P2PEvent) {
1839 if let Err(e) = tx.send(event) {
1840 tracing::trace!("Event broadcast has no receivers: {e}");
1841 }
1842}
1843
1844pub(crate) struct ParsedMessage {
1850 pub(crate) event: P2PEvent,
1852 pub(crate) authenticated_node_id: Option<PeerId>,
1854 pub(crate) user_agent: String,
1856 pub(crate) payload_len: usize,
1859}
1860
1861pub(crate) fn parse_protocol_message(bytes: &[u8], source: &str) -> Option<ParsedMessage> {
1871 let message: WireMessage = postcard::from_bytes(bytes).ok()?;
1872 let transport_source = source.parse::<SocketAddr>().ok().map(MultiAddr::quic);
1873
1874 let authenticated_node_id = if !message.signature.is_empty() {
1876 match verify_message_signature(&message) {
1877 Ok(peer_id) => {
1878 debug!(
1879 "Message from {} authenticated as app-level NodeId {}",
1880 source, peer_id
1881 );
1882 Some(peer_id)
1883 }
1884 Err(e) => {
1885 warn!(
1886 "Rejecting message from {}: signature verification failed: {}",
1887 source, e
1888 );
1889 return None;
1890 }
1891 }
1892 } else {
1893 None
1894 };
1895
1896 debug!(
1897 "Parsed P2PEvent::Message - topic: {}, source: {:?} (transport: {}, logical: {}), payload_len: {}",
1898 message.protocol,
1899 authenticated_node_id,
1900 source,
1901 message.from,
1902 message.data.len()
1903 );
1904
1905 let payload_len = message.data.len();
1906 Some(ParsedMessage {
1907 event: P2PEvent::Message {
1908 topic: message.protocol,
1909 source: authenticated_node_id,
1910 transport_source,
1911 timestamp: message.timestamp,
1912 data: message.data,
1913 },
1914 authenticated_node_id,
1915 payload_len,
1916 user_agent: message.user_agent,
1917 })
1918}
1919
1920fn verify_message_signature(message: &WireMessage) -> std::result::Result<PeerId, String> {
1927 let pubkey = MlDsaPublicKey::from_bytes(&message.public_key)
1928 .map_err(|e| format!("invalid public key: {e:?}"))?;
1929
1930 let peer_id = peer_id_from_public_key(&pubkey);
1931
1932 if message.from != peer_id {
1934 return Err(format!(
1935 "from field mismatch: message claims '{}' but public key derives '{}'",
1936 message.from, peer_id
1937 ));
1938 }
1939
1940 let signable = postcard::to_stdvec(&(
1941 &message.protocol,
1942 &message.data as &[u8],
1943 &message.from,
1944 message.timestamp,
1945 &message.user_agent,
1946 ))
1947 .map_err(|e| format!("failed to serialize signable bytes: {e}"))?;
1948
1949 let sig = MlDsaSignature::from_bytes(&message.signature)
1950 .map_err(|e| format!("invalid signature: {e:?}"))?;
1951
1952 let valid = crate::quantum_crypto::ml_dsa_verify(&pubkey, &signable, &sig)
1953 .map_err(|e| format!("verification error: {e}"))?;
1954
1955 if valid {
1956 Ok(peer_id)
1957 } else {
1958 Err("signature is invalid".to_string())
1959 }
1960}
1961
1962impl P2PNode {
1963 pub fn subscribe_events(&self) -> broadcast::Receiver<P2PEvent> {
1965 self.transport.subscribe_events()
1966 }
1967
1968 pub fn events(&self) -> broadcast::Receiver<P2PEvent> {
1970 self.subscribe_events()
1971 }
1972
1973 pub fn uptime(&self) -> Duration {
1975 self.start_time.elapsed()
1976 }
1977
1978 pub async fn health_check(&self) -> Result<()> {
1991 let peer_count = self.peer_count().await;
1992 if peer_count > self.config.max_connections {
1993 Err(protocol_error(format!(
1994 "Too many connections: {peer_count}"
1995 )))
1996 } else {
1997 Ok(())
1998 }
1999 }
2000
2001 pub fn dht_manager(&self) -> &Arc<DhtNetworkManager> {
2003 self.adaptive_dht.dht_manager()
2004 }
2005
2006 pub fn dht(&self) -> &Arc<DhtNetworkManager> {
2008 self.dht_manager()
2009 }
2010
2011 async fn connect_bootstrap_peers(
2018 &self,
2019 close_group_cache: Option<&CloseGroupCache>,
2020 ) -> Result<()> {
2021 let mut serial_addr_sets: Vec<Vec<MultiAddr>> = Vec::new();
2026 let mut parallel_addr_sets: Vec<Vec<MultiAddr>> = Vec::new();
2027 let mut seen_addresses = std::collections::HashSet::new();
2028
2029 if let Some(cache) = close_group_cache {
2035 let mut sorted_peers: Vec<&CachedCloseGroupPeer> = cache.peers.iter().collect();
2036 sorted_peers.sort_by(|a, b| {
2037 let score_ord = match b.trust.score.partial_cmp(&a.trust.score) {
2041 Some(ord) => ord,
2042 None => {
2043 if a.trust.score.is_nan() {
2044 std::cmp::Ordering::Greater } else {
2046 std::cmp::Ordering::Less }
2048 }
2049 };
2050 score_ord.then_with(|| {
2051 let da = self.peer_id.xor_distance(&a.peer_id);
2052 let db = self.peer_id.xor_distance(&b.peer_id);
2053 da.cmp(&db)
2054 })
2055 });
2056
2057 let mut added_from_close_group = 0usize;
2058 for peer in &sorted_peers {
2059 let new_addresses: Vec<MultiAddr> = peer
2060 .addresses
2061 .iter()
2062 .filter(|a| {
2063 a.dialable_socket_addr()
2064 .is_some_and(|sa| !seen_addresses.contains(&sa))
2065 })
2066 .cloned()
2067 .collect();
2068
2069 if !new_addresses.is_empty() {
2070 for addr in &new_addresses {
2071 if let Some(sa) = addr.socket_addr() {
2072 seen_addresses.insert(sa);
2073 }
2074 }
2075 serial_addr_sets.push(new_addresses);
2076 added_from_close_group += 1;
2077 }
2078 }
2079 if added_from_close_group > 0 {
2080 info!(
2081 "Added {} close group cache peers (highest trust first)",
2082 added_from_close_group
2083 );
2084 }
2085 }
2086
2087 if !self.config.bootstrap_peers.is_empty() {
2089 info!(
2090 "Using {} configured bootstrap peers (priority)",
2091 self.config.bootstrap_peers.len()
2092 );
2093 for multiaddr in &self.config.bootstrap_peers {
2094 let Some(socket_addr) = multiaddr.dialable_socket_addr() else {
2095 warn!("Skipping non-QUIC bootstrap peer: {}", multiaddr);
2096 continue;
2097 };
2098 seen_addresses.insert(socket_addr);
2099 parallel_addr_sets.push(vec![multiaddr.clone()]);
2100 }
2101 }
2102
2103 if serial_addr_sets.is_empty() && parallel_addr_sets.is_empty() {
2104 info!("No bootstrap peers configured");
2105 return Ok(());
2106 }
2107
2108 let identity_timeout = Duration::from_secs(BOOTSTRAP_IDENTITY_TIMEOUT_SECS);
2111 let mut successful_connections = 0;
2112 let mut connected_peer_ids: Vec<PeerId> = Vec::new();
2113
2114 let client_mode = matches!(self.config.mode, NodeMode::Client);
2116 for addrs in &serial_addr_sets {
2117 if let Some(peer_id) = self.dial_bootstrap_addr_set(addrs, identity_timeout).await {
2118 successful_connections += 1;
2119 connected_peer_ids.push(peer_id);
2120 if client_mode && successful_connections >= CLIENT_BOOTSTRAP_TARGET {
2121 debug!(
2122 "Client bootstrap target reached ({successful_connections} peers) — skipping remaining serial dials"
2123 );
2124 break;
2125 }
2126 }
2127 }
2128
2129 if !client_mode || successful_connections < CLIENT_BOOTSTRAP_TARGET {
2134 let mut parallel_stream =
2135 futures::stream::iter(parallel_addr_sets.into_iter().map(|addrs| async move {
2136 self.dial_bootstrap_addr_set(&addrs, identity_timeout).await
2137 }))
2138 .buffer_unordered(MAX_CONCURRENT_BOOTSTRAP_DIALS);
2139 while let Some(result) = parallel_stream.next().await {
2140 if let Some(peer_id) = result {
2141 successful_connections += 1;
2142 connected_peer_ids.push(peer_id);
2143 if client_mode && successful_connections >= CLIENT_BOOTSTRAP_TARGET {
2144 debug!(
2145 "Client bootstrap target reached ({successful_connections} peers) — cancelling pending dials"
2146 );
2147 break;
2148 }
2149 }
2150 }
2151 }
2155
2156 if successful_connections == 0 {
2157 tokio::time::sleep(std::time::Duration::from_secs(5)).await;
2161 let transport_peers = self.transport.connected_peers().await;
2162 if !transport_peers.is_empty() {
2163 info!(
2164 "No outbound bootstrap succeeded, but {} inbound peer(s) connected — proceeding with DHT bootstrap",
2165 transport_peers.len()
2166 );
2167 connected_peer_ids = transport_peers;
2168 successful_connections = connected_peer_ids.len();
2169 } else {
2170 warn!("Failed to connect to any bootstrap peers");
2171 return Ok(());
2174 }
2175 }
2176
2177 info!(
2178 "Successfully connected to {} bootstrap peers",
2179 successful_connections
2180 );
2181
2182 match self
2184 .dht_manager()
2185 .bootstrap_from_peers(&connected_peer_ids)
2186 .await
2187 {
2188 Ok(count) => info!("DHT peer discovery found {} peers", count),
2189 Err(e) => warn!("DHT peer discovery failed: {}", e),
2190 }
2191
2192 if matches!(self.config.mode, NodeMode::Node) {
2202 const SELF_LOOKUP_ROUNDS: u8 = 2;
2203 for i in 1..=SELF_LOOKUP_ROUNDS {
2204 if let Err(e) = self.dht_manager().trigger_self_lookup().await {
2205 warn!("Post-bootstrap self-lookup {i}/{SELF_LOOKUP_ROUNDS} failed: {e}");
2206 } else {
2207 debug!("Post-bootstrap self-lookup {i}/{SELF_LOOKUP_ROUNDS} completed");
2208 }
2209 }
2210 } else {
2211 debug!("Skipping post-bootstrap self-lookups (client mode)");
2212 }
2213
2214 self.is_bootstrapped.store(true, Ordering::SeqCst);
2217 info!(
2218 "Bootstrap complete: connected to {} peers, initiated {} discovery requests",
2219 successful_connections,
2220 connected_peer_ids.len()
2221 );
2222
2223 if let Some(ref dir) = self.config.close_group_cache_dir
2226 && let Err(e) = self.save_close_group_cache(dir).await
2227 {
2228 warn!("Failed to save close group cache after bootstrap: {e}");
2229 }
2230
2231 Ok(())
2232 }
2233
2234 async fn dial_bootstrap_addr_set(
2239 &self,
2240 addrs: &[MultiAddr],
2241 identity_timeout: Duration,
2242 ) -> Option<PeerId> {
2243 for addr in addrs {
2244 match self
2249 .transport
2250 .connect_peer_typed(addr, AddressType::Unverified)
2251 .await
2252 {
2253 Ok(channel_id) => match self
2254 .transport
2255 .wait_for_peer_identity(&channel_id, identity_timeout)
2256 .await
2257 {
2258 Ok(real_peer_id) => return Some(real_peer_id),
2259 Err(e) => {
2260 warn!(
2261 "Timeout waiting for identity from bootstrap peer {}: {}, \
2262 closing channel {}",
2263 addr, e, channel_id
2264 );
2265 self.disconnect_channel(&channel_id).await;
2266 }
2267 },
2268 Err(e) => {
2269 warn!("Failed to connect to bootstrap peer {}: {}", addr, e);
2270 }
2271 }
2272 }
2273 None
2274 }
2275
2276 async fn save_close_group_cache(&self, dir: &Path) -> anyhow::Result<()> {
2278 let key: crate::dht::Key = *self.peer_id.as_bytes();
2279 let k_value = self.config.dht_config.k_value;
2280 let close_group = self
2281 .dht_manager()
2282 .find_closest_nodes_local(&key, k_value)
2283 .await;
2284
2285 if close_group.is_empty() {
2286 debug!("No close group peers to save");
2287 return Ok(());
2288 }
2289
2290 let trust_engine = self.adaptive_dht.trust_engine();
2291 let now_epoch = SystemTime::now()
2292 .duration_since(UNIX_EPOCH)
2293 .map(|d| d.as_secs())
2294 .unwrap_or(0);
2295
2296 let peers: Vec<CachedCloseGroupPeer> = close_group
2297 .into_iter()
2298 .filter_map(|dht_node| {
2299 let score = trust_engine.score(&dht_node.peer_id);
2300 if !score.is_finite() {
2303 return None;
2304 }
2305 Some(CachedCloseGroupPeer {
2306 peer_id: dht_node.peer_id,
2307 addresses: dht_node.addresses,
2308 trust: TrustRecord {
2309 score,
2310 last_updated_epoch_secs: now_epoch,
2311 },
2312 })
2313 })
2314 .collect();
2315
2316 let peer_count = peers.len();
2317 let cache = CloseGroupCache {
2318 peers,
2319 saved_at_epoch_secs: now_epoch,
2320 };
2321
2322 cache.save_to_dir(dir).await?;
2323 info!(
2324 "Saved {} close group peers to cache in {}",
2325 peer_count,
2326 dir.display()
2327 );
2328 Ok(())
2329 }
2330
2331 }
2333
2334#[async_trait::async_trait]
2336#[allow(dead_code)]
2337pub trait NetworkSender: Send + Sync {
2338 async fn send_message(&self, peer_id: &PeerId, protocol: &str, data: Vec<u8>) -> Result<()>;
2340
2341 fn local_peer_id(&self) -> PeerId;
2343}
2344
2345pub(crate) fn register_new_channel(
2355 peers: &DashMap<String, PeerInfo>,
2356 channel_id: &str,
2357 remote_addr: &MultiAddr,
2358) {
2359 let peer_info = PeerInfo {
2360 channel_id: channel_id.to_owned(),
2361 addresses: vec![remote_addr.clone()],
2362 connected_at: tokio::time::Instant::now(),
2363 last_seen: tokio::time::Instant::now(),
2364 status: ConnectionStatus::Connected,
2365 protocols: vec!["p2p-core/1.0.0".to_string()],
2366 heartbeat_count: 0,
2367 };
2368 peers.insert(channel_id.to_owned(), peer_info);
2369}
2370
2371#[cfg(test)]
2372mod tests {
2373 use super::*;
2374 use std::time::Duration;
2376 use tokio::time::timeout;
2377
2378 const TEST_MAX_MESSAGE_SIZE: usize = 2 * 1024 * 1024;
2380
2381 fn create_test_node_config() -> NodeConfig {
2387 NodeConfig {
2388 local: true,
2389 port: 0,
2390 ipv6: true,
2391 bootstrap_peers: vec![],
2392 connection_timeout: Duration::from_secs(2),
2393 max_connections: 100,
2394 dht_config: DHTConfig::default(),
2395 diversity_config: None,
2396 max_message_size: None,
2397 node_identity: None,
2398 mode: NodeMode::default(),
2399 custom_user_agent: None,
2400 allow_loopback: true,
2401 adaptive_dht_config: AdaptiveDhtConfig::default(),
2402 close_group_cache_dir: None,
2403 }
2404 }
2405
2406 #[tokio::test]
2410 async fn test_node_config_default() {
2411 let config = NodeConfig::default();
2412
2413 assert_eq!(config.listen_addrs().len(), 2); assert_eq!(config.max_connections, 10000);
2415 assert_eq!(config.connection_timeout, Duration::from_secs(25));
2416 }
2417
2418 #[tokio::test]
2419 async fn test_dht_config_default() {
2420 let config = DHTConfig::default();
2421
2422 assert_eq!(config.k_value, 20);
2423 assert_eq!(config.alpha_value, 3);
2424 assert_eq!(config.refresh_interval, Duration::from_secs(600));
2425 }
2426
2427 #[test]
2428 fn test_connection_status_variants() {
2429 let connecting = ConnectionStatus::Connecting;
2430 let connected = ConnectionStatus::Connected;
2431 let disconnecting = ConnectionStatus::Disconnecting;
2432 let disconnected = ConnectionStatus::Disconnected;
2433 let failed = ConnectionStatus::Failed("test error".to_string());
2434
2435 assert_eq!(connecting, ConnectionStatus::Connecting);
2436 assert_eq!(connected, ConnectionStatus::Connected);
2437 assert_eq!(disconnecting, ConnectionStatus::Disconnecting);
2438 assert_eq!(disconnected, ConnectionStatus::Disconnected);
2439 assert_ne!(connecting, connected);
2440
2441 if let ConnectionStatus::Failed(msg) = failed {
2442 assert_eq!(msg, "test error");
2443 } else {
2444 panic!("Expected Failed status");
2445 }
2446 }
2447
2448 #[tokio::test]
2449 async fn test_node_creation() -> Result<()> {
2450 let config = create_test_node_config();
2451 let node = P2PNode::new(config).await?;
2452
2453 assert_eq!(node.peer_id().to_hex().len(), 64);
2455 assert!(!node.is_running());
2456 assert_eq!(node.peer_count().await, 0);
2457 assert!(node.connected_peers().await.is_empty());
2458
2459 Ok(())
2460 }
2461
2462 #[tokio::test]
2463 async fn test_node_lifecycle() -> Result<()> {
2464 let config = create_test_node_config();
2465 let node = P2PNode::new(config).await?;
2466
2467 assert!(!node.is_running());
2469
2470 node.start().await?;
2472 assert!(node.is_running());
2473
2474 let listen_addrs = node.listen_addrs().await;
2476 assert!(
2477 !listen_addrs.is_empty(),
2478 "Expected at least one listening address"
2479 );
2480
2481 node.stop().await?;
2483 assert!(!node.is_running());
2484
2485 Ok(())
2486 }
2487
2488 #[tokio::test]
2489 async fn test_peer_connection() -> Result<()> {
2490 let config1 = create_test_node_config();
2491 let config2 = create_test_node_config();
2492
2493 let node1 = P2PNode::new(config1).await?;
2494 let node2 = P2PNode::new(config2).await?;
2495
2496 node1.start().await?;
2497 node2.start().await?;
2498
2499 let node2_addr = node2
2500 .listen_addrs()
2501 .await
2502 .into_iter()
2503 .find(|a| a.is_ipv4())
2504 .ok_or_else(|| {
2505 P2PError::Network(crate::error::NetworkError::InvalidAddress(
2506 "Node 2 did not expose an IPv4 listen address".into(),
2507 ))
2508 })?;
2509
2510 let channel_id = node1.connect_peer(&node2_addr).await?;
2513
2514 assert!(node1.is_connection_active(&channel_id).await);
2517
2518 let peer_info = node1.transport.peer_info_by_channel(&channel_id).await;
2520 assert!(peer_info.is_some());
2521 let info = peer_info.expect("Peer info should exist after connect");
2522 assert_eq!(info.channel_id, channel_id);
2523 assert_eq!(info.status, ConnectionStatus::Connected);
2524 assert!(info.protocols.contains(&"p2p-foundation/1.0".to_string()));
2525
2526 node1.remove_channel(&channel_id).await;
2528 assert!(!node1.is_connection_active(&channel_id).await);
2529
2530 node1.stop().await?;
2531 node2.stop().await?;
2532
2533 Ok(())
2534 }
2535
2536 #[tokio::test]
2537 async fn test_connect_peer_rejects_tcp_multiaddr() -> Result<()> {
2538 let config = create_test_node_config();
2539 let node = P2PNode::new(config).await?;
2540
2541 let tcp_addr: MultiAddr = "/ip4/127.0.0.1/tcp/1".parse().unwrap();
2542 let result = node.connect_peer(&tcp_addr).await;
2543
2544 assert!(
2545 matches!(
2546 result,
2547 Err(P2PError::Network(
2548 crate::error::NetworkError::InvalidAddress(_)
2549 ))
2550 ),
2551 "TCP multiaddrs should be rejected before a QUIC dial is attempted, got: {:?}",
2552 result
2553 );
2554
2555 Ok(())
2556 }
2557
2558 #[cfg_attr(target_os = "windows", ignore)]
2565 #[tokio::test]
2566 async fn test_event_subscription() -> Result<()> {
2567 let identity1 =
2571 Arc::new(NodeIdentity::generate().expect("should generate identity for test node1"));
2572 let identity2 =
2573 Arc::new(NodeIdentity::generate().expect("should generate identity for test node2"));
2574
2575 let mut config1 = create_test_node_config();
2576 config1.ipv6 = false;
2577 config1.node_identity = Some(identity1);
2578
2579 let node2_peer_id = *identity2.peer_id();
2580 let mut config2 = create_test_node_config();
2581 config2.ipv6 = false;
2582 config2.node_identity = Some(identity2);
2583
2584 let node1 = P2PNode::new(config1).await?;
2585 let node2 = P2PNode::new(config2).await?;
2586
2587 node1.start().await?;
2588 node2.start().await?;
2589
2590 tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
2591
2592 let mut events = node2.subscribe_events();
2594
2595 let node2_addr = node2.local_addr().ok_or_else(|| {
2596 P2PError::Network(crate::error::NetworkError::ProtocolError(
2597 "No listening address".to_string().into(),
2598 ))
2599 })?;
2600
2601 let mut channel_id = None;
2603 for attempt in 0..3 {
2604 if attempt > 0 {
2605 tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
2606 }
2607 match timeout(Duration::from_secs(2), node1.connect_peer(&node2_addr)).await {
2608 Ok(Ok(id)) => {
2609 channel_id = Some(id);
2610 break;
2611 }
2612 Ok(Err(_)) | Err(_) => continue,
2613 }
2614 }
2615 let channel_id = channel_id.expect("Failed to connect after 3 attempts");
2616
2617 let target_peer_id = node1
2619 .wait_for_peer_identity(&channel_id, Duration::from_secs(2))
2620 .await?;
2621 assert_eq!(target_peer_id, node2_peer_id);
2622
2623 node1
2625 .send_message(&target_peer_id, "test-topic", b"hello".to_vec(), &[])
2626 .await?;
2627
2628 let event = timeout(Duration::from_secs(2), async {
2630 loop {
2631 match events.recv().await {
2632 Ok(P2PEvent::PeerConnected(id, _)) => return Ok(id),
2633 Ok(P2PEvent::Message { .. }) => continue, Ok(_) => continue,
2635 Err(e) => return Err(e),
2636 }
2637 }
2638 })
2639 .await;
2640 assert!(event.is_ok(), "Should receive PeerConnected event");
2641 let connected_peer_id = event.expect("Timed out").expect("Channel error");
2642 assert!(
2644 connected_peer_id.0.iter().any(|&b| b != 0),
2645 "PeerConnected should carry a non-zero peer ID"
2646 );
2647
2648 node1.stop().await?;
2649 node2.stop().await?;
2650
2651 Ok(())
2652 }
2653
2654 #[cfg_attr(target_os = "windows", ignore)]
2656 #[tokio::test]
2657 async fn test_message_sending() -> Result<()> {
2658 let mut config1 = create_test_node_config();
2660 config1.ipv6 = false;
2661 let node1 = P2PNode::new(config1).await?;
2662 node1.start().await?;
2663
2664 let mut config2 = create_test_node_config();
2665 config2.ipv6 = false;
2666 let node2 = P2PNode::new(config2).await?;
2667 node2.start().await?;
2668
2669 tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
2671
2672 let node2_addr = node2.local_addr().ok_or_else(|| {
2674 P2PError::Network(crate::error::NetworkError::ProtocolError(
2675 "No listening address".to_string().into(),
2676 ))
2677 })?;
2678
2679 let channel_id =
2681 match timeout(Duration::from_millis(500), node1.connect_peer(&node2_addr)).await {
2682 Ok(res) => res?,
2683 Err(_) => return Err(P2PError::Network(NetworkError::Timeout)),
2684 };
2685
2686 let target_peer_id = node1
2688 .wait_for_peer_identity(&channel_id, Duration::from_secs(2))
2689 .await?;
2690 assert_eq!(target_peer_id, node2.peer_id().clone());
2691
2692 let message_data = b"Hello, peer!".to_vec();
2694 let result = match timeout(
2695 Duration::from_millis(500),
2696 node1.send_message(&target_peer_id, "test-protocol", message_data, &[]),
2697 )
2698 .await
2699 {
2700 Ok(res) => res,
2701 Err(_) => return Err(P2PError::Network(NetworkError::Timeout)),
2702 };
2703 if let Err(e) = &result {
2706 assert!(!e.to_string().contains("not connected"), "Got error: {}", e);
2707 }
2708
2709 let non_existent_peer = PeerId::from_bytes([0xFFu8; 32]);
2711 let result = node1
2712 .send_message(&non_existent_peer, "test-protocol", vec![], &[])
2713 .await;
2714 assert!(result.is_err(), "Sending to non-existent peer should fail");
2715
2716 node1.stop().await?;
2717 node2.stop().await?;
2718
2719 Ok(())
2720 }
2721
2722 #[tokio::test]
2723 async fn test_remote_mcp_operations() -> Result<()> {
2724 let config = create_test_node_config();
2725 let node = P2PNode::new(config).await?;
2726
2727 node.start().await?;
2729 node.stop().await?;
2730 Ok(())
2731 }
2732
2733 #[tokio::test]
2734 async fn test_health_check() -> Result<()> {
2735 let config = create_test_node_config();
2736 let node = P2PNode::new(config).await?;
2737
2738 let result = node.health_check().await;
2740 assert!(result.is_ok());
2741
2742 Ok(())
2747 }
2748
2749 #[tokio::test]
2750 async fn test_node_uptime() -> Result<()> {
2751 let config = create_test_node_config();
2752 let node = P2PNode::new(config).await?;
2753
2754 let uptime1 = node.uptime();
2755 assert!(uptime1 >= Duration::from_secs(0));
2756
2757 tokio::time::sleep(Duration::from_millis(10)).await;
2759
2760 let uptime2 = node.uptime();
2761 assert!(uptime2 > uptime1);
2762
2763 Ok(())
2764 }
2765
2766 #[tokio::test]
2767 async fn test_node_config_access() -> Result<()> {
2768 let config = create_test_node_config();
2769 let node = P2PNode::new(config).await?;
2770
2771 let node_config = node.config();
2772 assert_eq!(node_config.max_connections, 100);
2773 Ok(())
2776 }
2777
2778 #[tokio::test]
2779 async fn test_mcp_server_access() -> Result<()> {
2780 let config = create_test_node_config();
2781 let _node = P2PNode::new(config).await?;
2782
2783 Ok(())
2785 }
2786
2787 #[tokio::test]
2788 async fn test_dht_access() -> Result<()> {
2789 let config = create_test_node_config();
2790 let node = P2PNode::new(config).await?;
2791
2792 let _dht = node.dht();
2794
2795 Ok(())
2796 }
2797
2798 #[tokio::test]
2799 async fn test_node_config_builder() -> Result<()> {
2800 let bootstrap: MultiAddr = "/ip4/127.0.0.1/udp/9000/quic".parse().unwrap();
2801
2802 let config = NodeConfig::builder()
2803 .local(true)
2804 .ipv6(true)
2805 .bootstrap_peer(bootstrap)
2806 .connection_timeout(Duration::from_secs(15))
2807 .max_connections(200)
2808 .max_message_size(TEST_MAX_MESSAGE_SIZE)
2809 .build()?;
2810
2811 assert_eq!(config.listen_addrs().len(), 2); assert!(config.local);
2813 assert!(config.ipv6);
2814 assert_eq!(config.bootstrap_peers.len(), 1);
2815 assert_eq!(config.connection_timeout, Duration::from_secs(15));
2816 assert_eq!(config.max_connections, 200);
2817 assert_eq!(config.max_message_size, Some(TEST_MAX_MESSAGE_SIZE));
2818 assert!(config.allow_loopback); Ok(())
2821 }
2822
2823 #[tokio::test]
2824 async fn test_bootstrap_peers() -> Result<()> {
2825 let mut config = create_test_node_config();
2826 config.bootstrap_peers = vec![
2827 crate::MultiAddr::from_ipv4(std::net::Ipv4Addr::LOCALHOST, 9200),
2828 crate::MultiAddr::from_ipv4(std::net::Ipv4Addr::LOCALHOST, 9201),
2829 ];
2830
2831 let node = P2PNode::new(config).await?;
2832
2833 node.start().await?;
2835
2836 let _peer_count = node.peer_count().await;
2840
2841 node.stop().await?;
2842 Ok(())
2843 }
2844
2845 #[tokio::test]
2846 async fn test_peer_info_structure() {
2847 let peer_info = PeerInfo {
2848 channel_id: "test_peer".to_string(),
2849 addresses: vec!["/ip4/127.0.0.1/tcp/9000".parse::<MultiAddr>().unwrap()],
2850 connected_at: Instant::now(),
2851 last_seen: Instant::now(),
2852 status: ConnectionStatus::Connected,
2853 protocols: vec!["test-protocol".to_string()],
2854 heartbeat_count: 0,
2855 };
2856
2857 assert_eq!(peer_info.channel_id, "test_peer");
2858 assert_eq!(peer_info.addresses.len(), 1);
2859 assert_eq!(peer_info.status, ConnectionStatus::Connected);
2860 assert_eq!(peer_info.protocols.len(), 1);
2861 }
2862
2863 #[tokio::test]
2864 async fn test_serialization() -> Result<()> {
2865 let config = create_test_node_config();
2867 let serialized = serde_json::to_string(&config)?;
2868 let deserialized: NodeConfig = serde_json::from_str(&serialized)?;
2869
2870 assert_eq!(config.local, deserialized.local);
2871 assert_eq!(config.port, deserialized.port);
2872 assert_eq!(config.ipv6, deserialized.ipv6);
2873 assert_eq!(config.bootstrap_peers, deserialized.bootstrap_peers);
2874
2875 Ok(())
2876 }
2877
2878 #[tokio::test]
2879 async fn test_get_channel_id_by_address_found() -> Result<()> {
2880 let config = create_test_node_config();
2881 let node = P2PNode::new(config).await?;
2882
2883 let test_channel_id = "peer_test_123".to_string();
2885 let test_address = "192.168.1.100:9000";
2886 let test_multiaddr = MultiAddr::quic(test_address.parse().unwrap());
2887
2888 let peer_info = PeerInfo {
2889 channel_id: test_channel_id.clone(),
2890 addresses: vec![test_multiaddr],
2891 connected_at: Instant::now(),
2892 last_seen: Instant::now(),
2893 status: ConnectionStatus::Connected,
2894 protocols: vec!["test-protocol".to_string()],
2895 heartbeat_count: 0,
2896 };
2897
2898 node.transport
2899 .inject_peer(test_channel_id.clone(), peer_info)
2900 .await;
2901
2902 let lookup_addr = MultiAddr::quic(test_address.parse().unwrap());
2904 let found_channel_id = node.get_channel_id_by_address(&lookup_addr).await;
2905 assert_eq!(found_channel_id, Some(test_channel_id));
2906
2907 Ok(())
2908 }
2909
2910 #[tokio::test]
2911 async fn test_get_channel_id_by_address_not_found() -> Result<()> {
2912 let config = create_test_node_config();
2913 let node = P2PNode::new(config).await?;
2914
2915 let unknown_addr = MultiAddr::quic("192.168.1.200:9000".parse().unwrap());
2917 let result = node.get_channel_id_by_address(&unknown_addr).await;
2918 assert_eq!(result, None);
2919
2920 Ok(())
2921 }
2922
2923 #[tokio::test]
2924 async fn test_get_channel_id_by_address_invalid_format() -> Result<()> {
2925 let config = create_test_node_config();
2926 let node = P2PNode::new(config).await?;
2927
2928 let ble_addr = MultiAddr::new(crate::address::TransportAddr::Ble {
2930 mac: [0x02, 0x00, 0x00, 0x00, 0x00, 0x01],
2931 psm: 0x0025,
2932 });
2933 let result = node.get_channel_id_by_address(&ble_addr).await;
2934 assert_eq!(result, None);
2935
2936 Ok(())
2937 }
2938
2939 #[tokio::test]
2940 async fn test_get_channel_id_by_address_multiple_peers() -> Result<()> {
2941 let config = create_test_node_config();
2942 let node = P2PNode::new(config).await?;
2943
2944 let peer1_id = "peer_1".to_string();
2946 let peer1_addr_str = "192.168.1.101:9001";
2947 let peer1_multiaddr = MultiAddr::quic(peer1_addr_str.parse().unwrap());
2948
2949 let peer2_id = "peer_2".to_string();
2950 let peer2_addr_str = "192.168.1.102:9002";
2951 let peer2_multiaddr = MultiAddr::quic(peer2_addr_str.parse().unwrap());
2952
2953 let peer1_info = PeerInfo {
2954 channel_id: peer1_id.clone(),
2955 addresses: vec![peer1_multiaddr],
2956 connected_at: Instant::now(),
2957 last_seen: Instant::now(),
2958 status: ConnectionStatus::Connected,
2959 protocols: vec!["test-protocol".to_string()],
2960 heartbeat_count: 0,
2961 };
2962
2963 let peer2_info = PeerInfo {
2964 channel_id: peer2_id.clone(),
2965 addresses: vec![peer2_multiaddr],
2966 connected_at: Instant::now(),
2967 last_seen: Instant::now(),
2968 status: ConnectionStatus::Connected,
2969 protocols: vec!["test-protocol".to_string()],
2970 heartbeat_count: 0,
2971 };
2972
2973 node.transport
2974 .inject_peer(peer1_id.clone(), peer1_info)
2975 .await;
2976 node.transport
2977 .inject_peer(peer2_id.clone(), peer2_info)
2978 .await;
2979
2980 let found_peer1 = node
2982 .get_channel_id_by_address(&MultiAddr::quic(peer1_addr_str.parse().unwrap()))
2983 .await;
2984 let found_peer2 = node
2985 .get_channel_id_by_address(&MultiAddr::quic(peer2_addr_str.parse().unwrap()))
2986 .await;
2987
2988 assert_eq!(found_peer1, Some(peer1_id));
2989 assert_eq!(found_peer2, Some(peer2_id));
2990
2991 Ok(())
2992 }
2993
2994 #[tokio::test]
2995 async fn test_list_active_connections_empty() -> Result<()> {
2996 let config = create_test_node_config();
2997 let node = P2PNode::new(config).await?;
2998
2999 let connections = node.list_active_connections().await;
3001 assert!(connections.is_empty());
3002
3003 Ok(())
3004 }
3005
3006 #[tokio::test]
3007 async fn test_list_active_connections_with_peers() -> Result<()> {
3008 let config = create_test_node_config();
3009 let node = P2PNode::new(config).await?;
3010
3011 let peer1_id = "peer_1".to_string();
3013 let peer1_addrs = vec![
3014 MultiAddr::quic("192.168.1.101:9001".parse().unwrap()),
3015 MultiAddr::quic("192.168.1.101:9002".parse().unwrap()),
3016 ];
3017
3018 let peer2_id = "peer_2".to_string();
3019 let peer2_addrs = vec![MultiAddr::quic("192.168.1.102:9003".parse().unwrap())];
3020
3021 let peer1_info = PeerInfo {
3022 channel_id: peer1_id.clone(),
3023 addresses: peer1_addrs.clone(),
3024 connected_at: Instant::now(),
3025 last_seen: Instant::now(),
3026 status: ConnectionStatus::Connected,
3027 protocols: vec!["test-protocol".to_string()],
3028 heartbeat_count: 0,
3029 };
3030
3031 let peer2_info = PeerInfo {
3032 channel_id: peer2_id.clone(),
3033 addresses: peer2_addrs.clone(),
3034 connected_at: Instant::now(),
3035 last_seen: Instant::now(),
3036 status: ConnectionStatus::Connected,
3037 protocols: vec!["test-protocol".to_string()],
3038 heartbeat_count: 0,
3039 };
3040
3041 node.transport
3042 .inject_peer(peer1_id.clone(), peer1_info)
3043 .await;
3044 node.transport
3045 .inject_peer(peer2_id.clone(), peer2_info)
3046 .await;
3047
3048 node.transport
3050 .inject_active_connection(peer1_id.clone())
3051 .await;
3052 node.transport
3053 .inject_active_connection(peer2_id.clone())
3054 .await;
3055
3056 let connections = node.list_active_connections().await;
3058 assert_eq!(connections.len(), 2);
3059
3060 let peer1_conn = connections.iter().find(|(id, _)| id == &peer1_id);
3062 let peer2_conn = connections.iter().find(|(id, _)| id == &peer2_id);
3063
3064 assert!(peer1_conn.is_some());
3065 assert!(peer2_conn.is_some());
3066
3067 assert_eq!(peer1_conn.unwrap().1, peer1_addrs);
3069 assert_eq!(peer2_conn.unwrap().1, peer2_addrs);
3070
3071 Ok(())
3072 }
3073
3074 #[tokio::test]
3075 async fn test_remove_channel_success() -> Result<()> {
3076 let config = create_test_node_config();
3077 let node = P2PNode::new(config).await?;
3078
3079 let channel_id = "peer_to_remove".to_string();
3081 let channel_peer_id = PeerId::from_name(&channel_id);
3082 let peer_info = PeerInfo {
3083 channel_id: channel_id.clone(),
3084 addresses: vec![MultiAddr::quic("192.168.1.100:9000".parse().unwrap())],
3085 connected_at: Instant::now(),
3086 last_seen: Instant::now(),
3087 status: ConnectionStatus::Connected,
3088 protocols: vec!["test-protocol".to_string()],
3089 heartbeat_count: 0,
3090 };
3091
3092 node.transport
3093 .inject_peer(channel_id.clone(), peer_info)
3094 .await;
3095 node.transport
3096 .inject_peer_to_channel(channel_peer_id, channel_id.clone())
3097 .await;
3098
3099 assert!(node.is_peer_connected(&channel_peer_id).await);
3101
3102 let removed = node.remove_channel(&channel_id).await;
3104 assert!(removed);
3105
3106 assert!(!node.is_peer_connected(&channel_peer_id).await);
3108
3109 Ok(())
3110 }
3111
3112 #[tokio::test]
3113 async fn test_remove_channel_nonexistent() -> Result<()> {
3114 let config = create_test_node_config();
3115 let node = P2PNode::new(config).await?;
3116
3117 let removed = node.remove_channel("nonexistent_peer").await;
3119 assert!(!removed);
3120
3121 Ok(())
3122 }
3123
3124 #[tokio::test]
3125 async fn test_is_peer_connected() -> Result<()> {
3126 let config = create_test_node_config();
3127 let node = P2PNode::new(config).await?;
3128
3129 let channel_id = "test_peer".to_string();
3130 let channel_peer_id = PeerId::from_name(&channel_id);
3131
3132 assert!(!node.is_peer_connected(&channel_peer_id).await);
3134
3135 let peer_info = PeerInfo {
3137 channel_id: channel_id.clone(),
3138 addresses: vec![MultiAddr::quic("192.168.1.100:9000".parse().unwrap())],
3139 connected_at: Instant::now(),
3140 last_seen: Instant::now(),
3141 status: ConnectionStatus::Connected,
3142 protocols: vec!["test-protocol".to_string()],
3143 heartbeat_count: 0,
3144 };
3145
3146 node.transport
3147 .inject_peer(channel_id.clone(), peer_info)
3148 .await;
3149 node.transport
3150 .inject_peer_to_channel(channel_peer_id, channel_id.clone())
3151 .await;
3152
3153 assert!(node.is_peer_connected(&channel_peer_id).await);
3155
3156 node.remove_channel(&channel_id).await;
3158
3159 assert!(!node.is_peer_connected(&channel_peer_id).await);
3161
3162 Ok(())
3163 }
3164
3165 #[test]
3166 fn test_normalize_ipv6_wildcard() {
3167 use std::net::{IpAddr, Ipv6Addr, SocketAddr};
3168
3169 let wildcard = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 8080);
3170 let normalized = normalize_wildcard_to_loopback(wildcard);
3171
3172 assert_eq!(normalized.ip(), IpAddr::V6(Ipv6Addr::LOCALHOST));
3173 assert_eq!(normalized.port(), 8080);
3174 }
3175
3176 #[test]
3177 fn test_normalize_ipv4_wildcard() {
3178 use std::net::{IpAddr, Ipv4Addr, SocketAddr};
3179
3180 let wildcard = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 9000);
3181 let normalized = normalize_wildcard_to_loopback(wildcard);
3182
3183 assert_eq!(normalized.ip(), IpAddr::V4(Ipv4Addr::LOCALHOST));
3184 assert_eq!(normalized.port(), 9000);
3185 }
3186
3187 #[test]
3188 fn test_normalize_specific_address_unchanged() {
3189 let specific: std::net::SocketAddr = "192.168.1.100:3000".parse().unwrap();
3190 let normalized = normalize_wildcard_to_loopback(specific);
3191
3192 assert_eq!(normalized, specific);
3193 }
3194
3195 #[test]
3196 fn test_normalize_loopback_unchanged() {
3197 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
3198
3199 let loopback_v6 = SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 5000);
3200 let normalized_v6 = normalize_wildcard_to_loopback(loopback_v6);
3201 assert_eq!(normalized_v6, loopback_v6);
3202
3203 let loopback_v4 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 5000);
3204 let normalized_v4 = normalize_wildcard_to_loopback(loopback_v4);
3205 assert_eq!(normalized_v4, loopback_v4);
3206 }
3207
3208 fn current_timestamp() -> u64 {
3212 std::time::SystemTime::now()
3213 .duration_since(std::time::UNIX_EPOCH)
3214 .map(|d| d.as_secs())
3215 .unwrap_or(0)
3216 }
3217
3218 fn make_wire_bytes(protocol: &str, data: Vec<u8>, from: &str, timestamp: u64) -> Vec<u8> {
3220 let msg = WireMessage {
3221 protocol: protocol.to_string(),
3222 data,
3223 from: PeerId::from_name(from),
3224 timestamp,
3225 user_agent: String::new(),
3226 public_key: Vec::new(),
3227 signature: Vec::new(),
3228 };
3229 postcard::to_stdvec(&msg).unwrap()
3230 }
3231
3232 fn make_signed_wire_bytes(
3234 identity: &NodeIdentity,
3235 protocol: &str,
3236 data: Vec<u8>,
3237 timestamp: u64,
3238 ) -> Vec<u8> {
3239 let from = *identity.peer_id();
3240 let user_agent = "test/1.0";
3241 let signable =
3242 postcard::to_stdvec(&(protocol, data.as_slice(), &from, timestamp, user_agent))
3243 .unwrap();
3244 let sig = identity.sign(&signable).expect("signing should succeed");
3245 let msg = WireMessage {
3246 protocol: protocol.to_string(),
3247 data,
3248 from,
3249 timestamp,
3250 user_agent: user_agent.to_string(),
3251 public_key: identity.public_key().as_bytes().to_vec(),
3252 signature: sig.as_bytes().to_vec(),
3253 };
3254 postcard::to_stdvec(&msg).unwrap()
3255 }
3256
3257 #[test]
3258 fn test_parse_protocol_message_uses_transport_peer_id_as_source() {
3259 let transport_id = "abcdef0123456789";
3262 let logical_id = "spoofed-logical-id";
3263 let bytes = make_wire_bytes("test/v1", vec![1, 2, 3], logical_id, current_timestamp());
3264
3265 let parsed =
3266 parse_protocol_message(&bytes, transport_id).expect("valid message should parse");
3267
3268 assert!(parsed.authenticated_node_id.is_none());
3270
3271 match parsed.event {
3272 P2PEvent::Message {
3273 topic,
3274 source,
3275 transport_source,
3276 timestamp: _,
3277 data,
3278 } => {
3279 assert!(source.is_none(), "unsigned message source must be None");
3280 assert!(
3281 transport_source.is_none(),
3282 "non-socket transport source should not produce an IP transport address"
3283 );
3284 assert_eq!(topic, "test/v1");
3285 assert_eq!(data, vec![1u8, 2, 3]);
3286 }
3287 other => panic!("expected P2PEvent::Message, got {:?}", other),
3288 }
3289 }
3290
3291 #[test]
3292 fn test_parse_protocol_message_rejects_invalid_bytes() {
3293 assert!(parse_protocol_message(b"not valid bincode", "peer-id").is_none());
3295 }
3296
3297 #[test]
3298 fn test_parse_protocol_message_rejects_truncated_message() {
3299 let full_bytes = make_wire_bytes("test/v1", vec![1, 2, 3], "sender", current_timestamp());
3301 let truncated = &full_bytes[..full_bytes.len() / 2];
3302 assert!(parse_protocol_message(truncated, "peer-id").is_none());
3303 }
3304
3305 #[test]
3306 fn test_parse_protocol_message_empty_payload() {
3307 let bytes = make_wire_bytes("ping", vec![], "sender", current_timestamp());
3308
3309 let parsed = parse_protocol_message(&bytes, "transport-peer")
3310 .expect("valid message with empty data should parse");
3311
3312 match parsed.event {
3313 P2PEvent::Message { data, .. } => assert!(data.is_empty()),
3314 other => panic!("expected P2PEvent::Message, got {:?}", other),
3315 }
3316 }
3317
3318 #[test]
3319 fn test_parse_protocol_message_records_ip_transport_source() {
3320 let bytes = make_wire_bytes("ping", vec![1], "sender", current_timestamp());
3321
3322 let parsed =
3323 parse_protocol_message(&bytes, "192.168.1.2:4567").expect("valid message should parse");
3324
3325 match parsed.event {
3326 P2PEvent::Message {
3327 transport_source, ..
3328 } => {
3329 assert_eq!(
3330 transport_source,
3331 Some(MultiAddr::quic("192.168.1.2:4567".parse().unwrap()))
3332 );
3333 }
3334 other => panic!("expected P2PEvent::Message, got {:?}", other),
3335 }
3336 }
3337
3338 #[test]
3339 fn test_parse_protocol_message_preserves_binary_payload() {
3340 let payload: Vec<u8> = (0..=255).collect();
3342 let bytes = make_wire_bytes("binary/v1", payload.clone(), "sender", current_timestamp());
3343
3344 let parsed = parse_protocol_message(&bytes, "peer-id")
3345 .expect("valid message with full byte range should parse");
3346
3347 match parsed.event {
3348 P2PEvent::Message { data, topic, .. } => {
3349 assert_eq!(topic, "binary/v1");
3350 assert_eq!(
3351 data, payload,
3352 "payload must survive bincode round-trip exactly"
3353 );
3354 }
3355 other => panic!("expected P2PEvent::Message, got {:?}", other),
3356 }
3357 }
3358
3359 #[test]
3360 fn test_parse_signed_message_verifies_and_uses_node_id() {
3361 let identity = NodeIdentity::generate().expect("should generate identity");
3362 let protocol = "test/signed";
3363 let data: Vec<u8> = vec![10, 20, 30];
3364 let from = *identity.peer_id();
3366 let timestamp = current_timestamp();
3367 let user_agent = "test/1.0";
3368
3369 let signable =
3371 postcard::to_stdvec(&(protocol, data.as_slice(), &from, timestamp, user_agent))
3372 .unwrap();
3373 let sig = identity.sign(&signable).expect("signing should succeed");
3374
3375 let msg = WireMessage {
3376 protocol: protocol.to_string(),
3377 data: data.clone(),
3378 from,
3379 timestamp,
3380 user_agent: user_agent.to_string(),
3381 public_key: identity.public_key().as_bytes().to_vec(),
3382 signature: sig.as_bytes().to_vec(),
3383 };
3384 let bytes = postcard::to_stdvec(&msg).unwrap();
3385
3386 let parsed =
3387 parse_protocol_message(&bytes, "transport-xyz").expect("signed message should parse");
3388
3389 let expected_peer_id = *identity.peer_id();
3390 assert_eq!(
3391 parsed.authenticated_node_id.as_ref(),
3392 Some(&expected_peer_id)
3393 );
3394
3395 match parsed.event {
3396 P2PEvent::Message { source, .. } => {
3397 assert_eq!(
3398 source.as_ref(),
3399 Some(&expected_peer_id),
3400 "source should be the verified PeerId"
3401 );
3402 }
3403 other => panic!("expected P2PEvent::Message, got {:?}", other),
3404 }
3405 }
3406
3407 #[test]
3408 fn test_parse_message_with_bad_signature_is_rejected() {
3409 let identity = NodeIdentity::generate().expect("should generate identity");
3410 let protocol = "test/bad-sig";
3411 let data: Vec<u8> = vec![1, 2, 3];
3412 let from = *identity.peer_id();
3413 let timestamp = current_timestamp();
3414 let user_agent = "test/1.0";
3415
3416 let signable =
3418 postcard::to_stdvec(&(protocol, data.as_slice(), &from, timestamp, user_agent))
3419 .unwrap();
3420 let sig = identity.sign(&signable).expect("signing should succeed");
3421
3422 let msg = WireMessage {
3424 protocol: protocol.to_string(),
3425 data: vec![99, 99, 99],
3426 from,
3427 timestamp,
3428 user_agent: user_agent.to_string(),
3429 public_key: identity.public_key().as_bytes().to_vec(),
3430 signature: sig.as_bytes().to_vec(),
3431 };
3432 let bytes = postcard::to_stdvec(&msg).unwrap();
3433
3434 assert!(
3435 parse_protocol_message(&bytes, "transport-xyz").is_none(),
3436 "message with bad signature should be rejected"
3437 );
3438 }
3439
3440 #[test]
3441 fn test_parse_message_with_mismatched_from_is_rejected() {
3442 let identity = NodeIdentity::generate().expect("should generate identity");
3443 let protocol = "test/from-mismatch";
3444 let data: Vec<u8> = vec![1, 2, 3];
3445 let fake_from = PeerId::from_bytes([0xDE; 32]);
3447 let timestamp = current_timestamp();
3448 let user_agent = "test/1.0";
3449
3450 let signable =
3451 postcard::to_stdvec(&(protocol, data.as_slice(), &fake_from, timestamp, user_agent))
3452 .unwrap();
3453 let sig = identity.sign(&signable).expect("signing should succeed");
3454
3455 let msg = WireMessage {
3456 protocol: protocol.to_string(),
3457 data,
3458 from: fake_from,
3459 timestamp,
3460 user_agent: user_agent.to_string(),
3461 public_key: identity.public_key().as_bytes().to_vec(),
3462 signature: sig.as_bytes().to_vec(),
3463 };
3464 let bytes = postcard::to_stdvec(&msg).unwrap();
3465
3466 assert!(
3467 parse_protocol_message(&bytes, "transport-xyz").is_none(),
3468 "message with mismatched from field should be rejected"
3469 );
3470 }
3471
3472 #[test]
3473 fn test_parse_protocol_message_accepts_arbitrary_timestamps() {
3474 let payload = vec![1, 2, 3];
3478
3479 let old_ts = current_timestamp().saturating_sub(36_000);
3481 let old_bytes = make_wire_bytes("test/old", payload.clone(), "sender", old_ts);
3482 assert!(
3483 parse_protocol_message(&old_bytes, "peer-id").is_some(),
3484 "should accept unsigned message with timestamp 10h in the past"
3485 );
3486
3487 let future_ts = current_timestamp().saturating_add(36_000);
3489 let future_bytes = make_wire_bytes("test/future", payload.clone(), "sender", future_ts);
3490 assert!(
3491 parse_protocol_message(&future_bytes, "peer-id").is_some(),
3492 "should accept unsigned message with timestamp 10h in the future"
3493 );
3494
3495 let identity = NodeIdentity::generate().expect("should generate identity");
3498 let signed_old =
3499 make_signed_wire_bytes(&identity, "test/signed-old", payload.clone(), old_ts);
3500 assert!(
3501 parse_protocol_message(&signed_old, "transport-xyz").is_some(),
3502 "should accept signed message with timestamp 10h in the past"
3503 );
3504
3505 let signed_future =
3506 make_signed_wire_bytes(&identity, "test/signed-future", payload, future_ts);
3507 assert!(
3508 parse_protocol_message(&signed_future, "transport-xyz").is_some(),
3509 "should accept signed message with timestamp 10h in the future"
3510 );
3511 }
3512
3513 #[test]
3514 fn test_parse_protocol_message_exposes_timestamp_on_event() {
3515 let ts: u64 = 1_234_567_890;
3519 let bytes = make_wire_bytes("test/ts", vec![9, 9, 9], "sender", ts);
3520 let parsed = parse_protocol_message(&bytes, "peer-id").expect("valid message should parse");
3521 match parsed.event {
3522 P2PEvent::Message { timestamp, .. } => {
3523 assert_eq!(timestamp, ts, "P2PEvent::Message.timestamp must round-trip");
3524 }
3525 other => panic!("expected P2PEvent::Message, got {:?}", other),
3526 }
3527 }
3528
3529 #[test]
3530 fn test_signed_message_timestamp_is_signature_covered() {
3531 let identity = NodeIdentity::generate().expect("should generate identity");
3535 let ts: u64 = 1_700_000_000;
3536 let signed = make_signed_wire_bytes(&identity, "test/sig", vec![1, 2, 3], ts);
3537
3538 let parsed = parse_protocol_message(&signed, "transport-xyz")
3540 .expect("unmodified signed message should parse");
3541 assert!(parsed.authenticated_node_id.is_some());
3542
3543 let mut tampered: WireMessage =
3545 postcard::from_bytes(&signed).expect("signed bytes must deserialize");
3546 tampered.timestamp = ts.wrapping_add(1);
3547 let tampered_bytes = postcard::to_stdvec(&tampered).expect("re-serialize");
3548
3549 assert!(
3550 parse_protocol_message(&tampered_bytes, "transport-xyz").is_none(),
3551 "timestamp-only mutation on a signed message must fail signature verification"
3552 );
3553 }
3554}