1use std::net::SocketAddr;
7use std::time::Duration;
8use std::collections::HashSet;
9use thiserror::Error;
10
11use ed25519_dalek::{SigningKey as Ed25519SecretKey, VerifyingKey as Ed25519PublicKey};
12use crate::nat_traversal::{BootstrapNode, NatTraversalConfig};
13
14mod validation;
15
16#[derive(Error, Debug)]
18pub enum ConfigError {
19 #[error("Invalid bootstrap node configuration: {0}")]
20 InvalidBootstrapNode(String),
21
22 #[error("Invalid network configuration: {0}")]
23 InvalidNetwork(String),
24
25 #[error("Invalid timeout configuration: {0}")]
26 InvalidTimeout(String),
27
28 #[error("Invalid role configuration: {0}")]
29 InvalidRole(String),
30
31 #[error("Missing required configuration: {0}")]
32 MissingRequiredConfig(String),
33
34 #[error("Configuration value out of range: {0}")]
35 ValueOutOfRange(String),
36
37 #[error("Invalid address format: {0}")]
38 InvalidAddress(String),
39
40 #[error("Platform-specific configuration error: {0}")]
41 PlatformSpecific(String),
42}
43
44pub type ConfigResult<T> = Result<T, ConfigError>;
46
47#[derive(Clone, Debug)]
69pub struct P2PConfig {
70 pub(crate) bootstrap_nodes: Vec<BootstrapNode>,
72
73 pub(crate) keypair: (Ed25519SecretKey, Ed25519PublicKey),
75
76 pub(crate) nat_traversal_enabled: bool,
78
79 pub(crate) listen_address: SocketAddr,
81
82 pub(crate) connection_timeout: Duration,
84
85 pub(crate) max_connection_attempts: u32,
87
88 pub(crate) max_concurrent_connections: u32,
90
91 pub(crate) nat_traversal_config: Option<NatTraversalConfig>,
93}
94
95impl P2PConfig {
96 pub fn builder() -> P2PConfigBuilder {
106 P2PConfigBuilder::new()
107 }
108
109 pub fn bootstrap_nodes(&self) -> &[BootstrapNode] {
111 &self.bootstrap_nodes
112 }
113
114 pub fn keypair(&self) -> &(Ed25519SecretKey, Ed25519PublicKey) {
116 &self.keypair
117 }
118
119 pub fn nat_traversal_enabled(&self) -> bool {
121 self.nat_traversal_enabled
122 }
123
124 pub fn listen_address(&self) -> SocketAddr {
126 self.listen_address
127 }
128
129 pub fn connection_timeout(&self) -> Duration {
131 self.connection_timeout
132 }
133
134 pub fn max_connection_attempts(&self) -> u32 {
136 self.max_connection_attempts
137 }
138
139 pub fn max_concurrent_connections(&self) -> u32 {
141 self.max_concurrent_connections
142 }
143
144 pub fn nat_traversal_config(&self) -> Option<&NatTraversalConfig> {
146 self.nat_traversal_config.as_ref()
147 }
148}
149
150#[derive(Clone, Debug)]
171pub struct P2PConfigBuilder {
172 bootstrap_nodes: Vec<BootstrapNode>,
173 keypair: Option<(Ed25519SecretKey, Ed25519PublicKey)>,
174 nat_traversal_enabled: bool,
175 listen_address: Option<SocketAddr>,
176 connection_timeout: Duration,
177 max_connection_attempts: u32,
178 max_concurrent_connections: u32,
179 nat_traversal_config: Option<NatTraversalConfig>,
180}
181
182impl P2PConfigBuilder {
183 pub fn new() -> Self {
185 Self {
186 bootstrap_nodes: Vec::new(),
187 keypair: None,
188 nat_traversal_enabled: true,
189 listen_address: None,
190 connection_timeout: Duration::from_secs(30),
191 max_connection_attempts: 3,
192 max_concurrent_connections: 100,
193 nat_traversal_config: None,
194 }
195 }
196
197 pub fn with_bootstrap_nodes<T: Into<Vec<BootstrapNode>>>(&mut self, nodes: T) -> &mut Self {
202 self.bootstrap_nodes = nodes.into();
203 self
204 }
205
206 pub fn add_bootstrap_node(&mut self, node: BootstrapNode) -> &mut Self {
208 self.bootstrap_nodes.push(node);
209 self
210 }
211
212 pub fn with_keypair(&mut self, keypair: (Ed25519SecretKey, Ed25519PublicKey)) -> &mut Self {
214 self.keypair = Some(keypair);
215 self
216 }
217
218 pub fn with_nat_traversal(&mut self, enabled: bool) -> &mut Self {
220 self.nat_traversal_enabled = enabled;
221 self
222 }
223
224 pub fn with_listen_address(&mut self, address: SocketAddr) -> &mut Self {
226 self.listen_address = Some(address);
227 self
228 }
229
230 pub fn with_connection_timeout(&mut self, timeout: Duration) -> &mut Self {
232 self.connection_timeout = timeout;
233 self
234 }
235
236 pub fn with_max_connection_attempts(&mut self, attempts: u32) -> &mut Self {
238 self.max_connection_attempts = attempts;
239 self
240 }
241
242 pub fn with_max_concurrent_connections(&mut self, connections: u32) -> &mut Self {
244 self.max_concurrent_connections = connections;
245 self
246 }
247
248 pub fn with_nat_traversal_config(&mut self, config: NatTraversalConfig) -> &mut Self {
250 self.nat_traversal_config = Some(config);
251 self
252 }
253
254 pub fn build(&self) -> ConfigResult<P2PConfig> {
259 if self.nat_traversal_enabled && self.bootstrap_nodes.is_empty() {
261 return Err(ConfigError::MissingRequiredConfig(
262 "At least one bootstrap node is required when NAT traversal is enabled".to_string()
263 ));
264 }
265
266 let mut seen = HashSet::new();
268 for (i, node) in self.bootstrap_nodes.iter().enumerate() {
269 if !seen.insert(node.address) {
270 return Err(ConfigError::InvalidBootstrapNode(
271 format!("Duplicate bootstrap node at index {}: {}", i, node.address)
272 ));
273 }
274 }
275
276 let keypair = self.keypair.clone().ok_or_else(|| {
278 ConfigError::MissingRequiredConfig("Keypair is required".to_string())
279 })?;
280
281 let listen_address = self.listen_address.unwrap_or_else(|| {
283 "0.0.0.0:0".parse().unwrap()
285 });
286
287 if self.connection_timeout < Duration::from_secs(1) || self.connection_timeout > Duration::from_secs(300) {
289 return Err(ConfigError::ValueOutOfRange(
290 format!("Connection timeout must be between 1 and 300 seconds, got {:?}", self.connection_timeout)
291 ));
292 }
293
294 if self.max_connection_attempts == 0 || self.max_connection_attempts > 10 {
296 return Err(ConfigError::ValueOutOfRange(
297 format!("Maximum connection attempts must be between 1 and 10, got {}", self.max_connection_attempts)
298 ));
299 }
300
301 if self.max_concurrent_connections == 0 || self.max_concurrent_connections > 1000 {
303 return Err(ConfigError::ValueOutOfRange(
304 format!("Maximum concurrent connections must be between 1 and 1000, got {}", self.max_concurrent_connections)
305 ));
306 }
307
308 Ok(P2PConfig {
310 bootstrap_nodes: self.bootstrap_nodes.clone(),
311 keypair,
312 nat_traversal_enabled: self.nat_traversal_enabled,
313 listen_address,
314 connection_timeout: self.connection_timeout,
315 max_connection_attempts: self.max_connection_attempts,
316 max_concurrent_connections: self.max_concurrent_connections,
317 nat_traversal_config: self.nat_traversal_config.clone(),
318 })
319 }
320}
321
322impl Default for P2PConfigBuilder {
323 fn default() -> Self {
324 Self::new()
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331 use std::net::{IpAddr, Ipv4Addr};
332 use crate::nat_traversal::BootstrapNode;
333 use crate::crypto::raw_public_keys::key_utils;
334
335 #[test]
336 fn test_builder_with_valid_config() {
337 let keypair = key_utils::generate_ed25519_keypair();
338 let bootstrap_node = BootstrapNode::new("127.0.0.1:9000".parse().unwrap());
339
340 let config = P2PConfigBuilder::new()
341 .with_keypair(keypair)
342 .add_bootstrap_node(bootstrap_node)
343 .with_nat_traversal(true)
344 .with_listen_address(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8000))
345 .with_connection_timeout(Duration::from_secs(60))
346 .build()
347 .unwrap();
348
349 assert_eq!(config.bootstrap_nodes.len(), 1);
350 assert_eq!(config.bootstrap_nodes[0].address.to_string(), "127.0.0.1:9000");
351 assert!(config.nat_traversal_enabled);
352 assert_eq!(config.listen_address.to_string(), "127.0.0.1:8000");
353 assert_eq!(config.connection_timeout, Duration::from_secs(60));
354 }
355
356 #[test]
357 fn test_builder_missing_keypair() {
358 let bootstrap_node = BootstrapNode::new("127.0.0.1:9000".parse().unwrap());
359
360 let result = P2PConfigBuilder::new()
361 .add_bootstrap_node(bootstrap_node)
362 .with_nat_traversal(true)
363 .build();
364
365 assert!(result.is_err());
366 match result {
367 Err(ConfigError::MissingRequiredConfig(msg)) => {
368 assert!(msg.contains("Keypair is required"));
369 }
370 _ => panic!("Expected MissingRequiredConfig error"),
371 }
372 }
373
374 #[test]
375 fn test_builder_missing_bootstrap_nodes() {
376 let keypair = key_utils::generate_ed25519_keypair();
377
378 let result = P2PConfigBuilder::new()
379 .with_keypair(keypair)
380 .with_nat_traversal(true)
381 .build();
382
383 assert!(result.is_err());
384 match result {
385 Err(ConfigError::MissingRequiredConfig(msg)) => {
386 assert!(msg.contains("bootstrap node"));
387 }
388 _ => panic!("Expected MissingRequiredConfig error"),
389 }
390 }
391
392 #[test]
393 fn test_builder_duplicate_bootstrap_nodes() {
394 let keypair = key_utils::generate_ed25519_keypair();
395 let addr = "127.0.0.1:9000".parse().unwrap();
396 let bootstrap_node1 = BootstrapNode::new(addr);
397 let bootstrap_node2 = BootstrapNode::new(addr);
398
399 let result = P2PConfigBuilder::new()
400 .with_keypair(keypair)
401 .add_bootstrap_node(bootstrap_node1)
402 .add_bootstrap_node(bootstrap_node2)
403 .build();
404
405 assert!(result.is_err());
406 match result {
407 Err(ConfigError::InvalidBootstrapNode(msg)) => {
408 assert!(msg.contains("Duplicate bootstrap node"));
409 }
410 _ => panic!("Expected InvalidBootstrapNode error"),
411 }
412 }
413
414 #[test]
415 fn test_builder_invalid_connection_timeout() {
416 let keypair = key_utils::generate_ed25519_keypair();
417 let bootstrap_node = BootstrapNode::new("127.0.0.1:9000".parse().unwrap());
418
419 let result = P2PConfigBuilder::new()
420 .with_keypair(keypair)
421 .add_bootstrap_node(bootstrap_node)
422 .with_connection_timeout(Duration::from_millis(500))
423 .build();
424
425 assert!(result.is_err());
426 match result {
427 Err(ConfigError::ValueOutOfRange(msg)) => {
428 assert!(msg.contains("Connection timeout"));
429 }
430 _ => panic!("Expected ValueOutOfRange error"),
431 }
432 }
433
434 #[test]
435 fn test_builder_nat_traversal_disabled() {
436 let keypair = key_utils::generate_ed25519_keypair();
437
438 let config = P2PConfigBuilder::new()
439 .with_keypair(keypair)
440 .with_nat_traversal(false)
441 .build()
442 .unwrap();
443
444 assert!(!config.nat_traversal_enabled);
445 assert_eq!(config.bootstrap_nodes.len(), 0);
446 }
447
448 #[test]
449 fn test_config_getters() {
450 let keypair = key_utils::generate_ed25519_keypair();
451 let bootstrap_node = BootstrapNode::new("127.0.0.1:9000".parse().unwrap());
452 let listen_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8000);
453 let timeout = Duration::from_secs(60);
454
455 let config = P2PConfigBuilder::new()
456 .with_keypair(keypair)
457 .add_bootstrap_node(bootstrap_node)
458 .with_listen_address(listen_address)
459 .with_connection_timeout(timeout)
460 .with_max_connection_attempts(5)
461 .with_max_concurrent_connections(200)
462 .build()
463 .unwrap();
464
465 assert_eq!(config.bootstrap_nodes().len(), 1);
466 assert_eq!(config.listen_address(), listen_address);
467 assert_eq!(config.connection_timeout(), timeout);
468 assert_eq!(config.max_connection_attempts(), 5);
469 assert_eq!(config.max_concurrent_connections(), 200);
470 }
471}