1use serde::{Deserialize, Serialize};
4use std::collections::{HashMap, HashSet};
5use std::fmt;
6use thiserror::Error;
7
8use crate::message::{Message, MessageType, ProtocolVersion};
9
10pub type CustomMigrationFn = Box<dyn Fn(&Message) -> Result<Message, VersionError> + Send + Sync>;
12
13#[derive(Debug, Error)]
15pub enum VersionError {
16 #[error("Unsupported protocol version: {version:?}")]
18 UnsupportedVersion { version: ProtocolVersion },
19
20 #[error("Incompatible protocol versions: {local:?} vs {remote:?}")]
22 IncompatibleVersions {
23 local: ProtocolVersion,
24 remote: ProtocolVersion,
25 },
26
27 #[error("Feature '{feature}' not available in version {version:?}")]
29 FeatureNotAvailable {
30 feature: String,
31 version: ProtocolVersion,
32 },
33
34 #[error("Migration from {from:?} to {to:?} failed: {reason}")]
36 MigrationFailed {
37 from: ProtocolVersion,
38 to: ProtocolVersion,
39 reason: String,
40 },
41
42 #[error("Serialization error: {reason}")]
44 SerializationError { reason: String },
45}
46
47#[derive(Debug, Clone)]
49pub struct VersionRegistry {
50 supported_versions: Vec<VersionInfo>,
52 feature_matrix: HashMap<ProtocolVersion, HashSet<String>>,
54 message_compatibility: HashMap<ProtocolVersion, HashSet<MessageType>>,
56 migration_paths: HashMap<(ProtocolVersion, ProtocolVersion), MigrationStrategy>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct VersionInfo {
63 pub version: ProtocolVersion,
65 pub name: String,
67 pub release_date: String,
69 pub features: Vec<String>,
71 pub deprecated_features: Vec<String>,
73 pub security_requirements: SecurityRequirements,
75 pub compatibility: CompatibilityInfo,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct SecurityRequirements {
82 pub min_key_sizes: HashMap<String, u32>,
84 pub required_algorithms: Vec<String>,
86 pub forbidden_algorithms: Vec<String>,
88 pub quantum_resistant: bool,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct CompatibilityInfo {
95 pub compatible_with: Vec<ProtocolVersion>,
97 pub min_supported_version: ProtocolVersion,
99 pub breaking_changes: Vec<String>,
101 pub migration_notes: Vec<String>,
103}
104
105pub enum MigrationStrategy {
107 Direct,
109 Transform(fn(&Message) -> Result<Message, VersionError>),
111 Custom(CustomMigrationFn),
113 NotSupported,
115}
116
117impl std::fmt::Debug for MigrationStrategy {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 match self {
120 MigrationStrategy::Direct => write!(f, "Direct"),
121 MigrationStrategy::Transform(_) => write!(f, "Transform(<function>)"),
122 MigrationStrategy::Custom(_) => write!(f, "Custom(<closure>)"),
123 MigrationStrategy::NotSupported => write!(f, "NotSupported"),
124 }
125 }
126}
127
128impl Clone for MigrationStrategy {
129 fn clone(&self) -> Self {
130 match self {
131 MigrationStrategy::Direct => MigrationStrategy::Direct,
132 MigrationStrategy::Transform(f) => MigrationStrategy::Transform(*f),
133 MigrationStrategy::Custom(_) => {
134 MigrationStrategy::NotSupported
138 }
139 MigrationStrategy::NotSupported => MigrationStrategy::NotSupported,
140 }
141 }
142}
143
144pub struct VersionManager {
146 registry: VersionRegistry,
148 current_version: ProtocolVersion,
150 preferences: VersionPreferences,
152}
153
154#[derive(Debug, Clone)]
156pub struct VersionPreferences {
157 pub preferred_version: ProtocolVersion,
159 pub min_version: ProtocolVersion,
161 pub max_version: ProtocolVersion,
163 pub allow_downgrade: bool,
165 pub required_features: Vec<String>,
167 pub preferred_features: Vec<String>,
169}
170
171impl VersionRegistry {
172 pub fn new() -> Self {
174 let mut registry = Self {
175 supported_versions: Vec::new(),
176 feature_matrix: HashMap::new(),
177 message_compatibility: HashMap::new(),
178 migration_paths: HashMap::new(),
179 };
180
181 registry.register_default_versions();
183 registry
184 }
185
186 fn register_default_versions(&mut self) {
188 let v1_0_0 = VersionInfo {
190 version: ProtocolVersion {
191 major: 1,
192 minor: 0,
193 patch: 0,
194 features: vec![],
195 },
196 name: "Genesis".to_string(),
197 release_date: "2024-01-01".to_string(),
198 features: vec![
199 "basic-messaging".to_string(),
200 "quantum-resistant-crypto".to_string(),
201 "dag-consensus".to_string(),
202 "anonymous-routing".to_string(),
203 ],
204 deprecated_features: vec![],
205 security_requirements: SecurityRequirements {
206 min_key_sizes: [("ml-dsa".to_string(), 2048), ("ml-kem".to_string(), 768)]
207 .into_iter()
208 .collect(),
209 required_algorithms: vec![
210 "ML-DSA".to_string(),
211 "ML-KEM-768".to_string(),
212 "BLAKE3".to_string(),
213 ],
214 forbidden_algorithms: vec![
215 "RSA".to_string(),
216 "ECDSA".to_string(),
217 "DH".to_string(),
218 ],
219 quantum_resistant: true,
220 },
221 compatibility: CompatibilityInfo {
222 compatible_with: vec![],
223 min_supported_version: ProtocolVersion {
224 major: 1,
225 minor: 0,
226 patch: 0,
227 features: vec![],
228 },
229 breaking_changes: vec![],
230 migration_notes: vec!["Initial version".to_string()],
231 },
232 };
233
234 let v1_1_0 = VersionInfo {
236 version: ProtocolVersion {
237 major: 1,
238 minor: 1,
239 patch: 0,
240 features: vec![],
241 },
242 name: "Shadow".to_string(),
243 release_date: "2024-06-01".to_string(),
244 features: vec![
245 "basic-messaging".to_string(),
246 "quantum-resistant-crypto".to_string(),
247 "dag-consensus".to_string(),
248 "anonymous-routing".to_string(),
249 "dark-addressing".to_string(),
250 "enhanced-privacy".to_string(),
251 ],
252 deprecated_features: vec![],
253 security_requirements: SecurityRequirements {
254 min_key_sizes: [("ml-dsa".to_string(), 2048), ("ml-kem".to_string(), 768)]
255 .into_iter()
256 .collect(),
257 required_algorithms: vec![
258 "ML-DSA".to_string(),
259 "ML-KEM-768".to_string(),
260 "BLAKE3".to_string(),
261 "HQC".to_string(),
262 ],
263 forbidden_algorithms: vec![
264 "RSA".to_string(),
265 "ECDSA".to_string(),
266 "DH".to_string(),
267 ],
268 quantum_resistant: true,
269 },
270 compatibility: CompatibilityInfo {
271 compatible_with: vec![v1_0_0.version.clone()],
272 min_supported_version: v1_0_0.version.clone(),
273 breaking_changes: vec![],
274 migration_notes: vec![
275 "Backward compatible with 1.0.0".to_string(),
276 "New dark addressing features are optional".to_string(),
277 ],
278 },
279 };
280
281 self.register_version(v1_0_0);
282 self.register_version(v1_1_0);
283
284 self.setup_migration_paths();
286 }
287
288 pub fn register_version(&mut self, version_info: VersionInfo) {
290 let version = version_info.version.clone();
291
292 self.supported_versions.push(version_info.clone());
294
295 let features: HashSet<String> = version_info.features.into_iter().collect();
297 self.feature_matrix.insert(version.clone(), features);
298
299 let message_types: HashSet<MessageType> = [
301 MessageType::Handshake(crate::message::HandshakeType::Init),
302 MessageType::Handshake(crate::message::HandshakeType::Response),
303 MessageType::Handshake(crate::message::HandshakeType::Complete),
304 MessageType::Handshake(crate::message::HandshakeType::VersionNegotiation),
305 MessageType::Control(crate::message::ControlMessageType::Ping),
306 MessageType::Control(crate::message::ControlMessageType::Pong),
307 MessageType::Consensus(crate::message::ConsensusMessageType::VertexProposal),
308 MessageType::Consensus(crate::message::ConsensusMessageType::Vote),
309 ]
310 .into_iter()
311 .collect();
312 self.message_compatibility.insert(version, message_types);
313 }
314
315 fn setup_migration_paths(&mut self) {
317 let v1_0_0 = ProtocolVersion {
318 major: 1,
319 minor: 0,
320 patch: 0,
321 features: vec![],
322 };
323 let v1_1_0 = ProtocolVersion {
324 major: 1,
325 minor: 1,
326 patch: 0,
327 features: vec![],
328 };
329
330 self.migration_paths
332 .insert((v1_0_0.clone(), v1_1_0.clone()), MigrationStrategy::Direct);
333
334 self.migration_paths.insert(
336 (v1_1_0, v1_0_0),
337 MigrationStrategy::Transform(downgrade_1_1_to_1_0),
338 );
339 }
340
341 pub fn is_supported(&self, version: &ProtocolVersion) -> bool {
343 self.supported_versions
344 .iter()
345 .any(|v| &v.version == version)
346 }
347
348 pub fn get_version_info(&self, version: &ProtocolVersion) -> Option<&VersionInfo> {
350 self.supported_versions
351 .iter()
352 .find(|v| &v.version == version)
353 }
354
355 pub fn get_supported_versions(&self) -> &[VersionInfo] {
357 &self.supported_versions
358 }
359
360 pub fn is_feature_supported(&self, version: &ProtocolVersion, feature: &str) -> bool {
362 self.feature_matrix
363 .get(version)
364 .map(|features| features.contains(feature))
365 .unwrap_or(false)
366 }
367
368 pub fn are_compatible(&self, v1: &ProtocolVersion, v2: &ProtocolVersion) -> bool {
370 if v1 == v2 {
372 return true;
373 }
374
375 if let Some(v1_info) = self.get_version_info(v1) {
377 if v1_info.compatibility.compatible_with.contains(v2) {
378 return true;
379 }
380 }
381
382 if let Some(v2_info) = self.get_version_info(v2) {
383 if v2_info.compatibility.compatible_with.contains(v1) {
384 return true;
385 }
386 }
387
388 v1.is_compatible(v2)
390 }
391
392 pub fn get_migration_strategy(
394 &self,
395 from: &ProtocolVersion,
396 to: &ProtocolVersion,
397 ) -> Option<&MigrationStrategy> {
398 self.migration_paths.get(&(from.clone(), to.clone()))
399 }
400
401 pub fn find_best_compatible_version(
403 &self,
404 available_versions: &[ProtocolVersion],
405 preferences: &VersionPreferences,
406 ) -> Option<ProtocolVersion> {
407 let mut compatible: Vec<&ProtocolVersion> = available_versions
409 .iter()
410 .filter(|v| {
411 v.major >= preferences.min_version.major &&
413 v.major <= preferences.max_version.major &&
414 self.is_supported(v) &&
416 preferences.required_features.iter().all(|feature| {
418 self.is_feature_supported(v, feature)
419 })
420 })
421 .collect();
422
423 if compatible.is_empty() {
424 return None;
425 }
426
427 compatible.sort_by(|a, b| {
429 if **a == preferences.preferred_version {
430 std::cmp::Ordering::Less
431 } else if **b == preferences.preferred_version {
432 std::cmp::Ordering::Greater
433 } else {
434 b.major
436 .cmp(&a.major)
437 .then(b.minor.cmp(&a.minor))
438 .then(b.patch.cmp(&a.patch))
439 }
440 });
441
442 Some(compatible[0].clone())
443 }
444}
445
446impl VersionManager {
447 pub fn new(current_version: ProtocolVersion) -> Self {
449 let preferences = VersionPreferences {
450 preferred_version: current_version.clone(),
451 min_version: ProtocolVersion {
452 major: 1,
453 minor: 0,
454 patch: 0,
455 features: vec![],
456 },
457 max_version: ProtocolVersion {
458 major: 1,
459 minor: 1,
460 patch: 0,
461 features: vec![],
462 },
463 allow_downgrade: true,
464 required_features: vec![
465 "quantum-resistant-crypto".to_string(),
466 "dag-consensus".to_string(),
467 ],
468 preferred_features: vec![
469 "anonymous-routing".to_string(),
470 "dark-addressing".to_string(),
471 ],
472 };
473
474 Self {
475 registry: VersionRegistry::new(),
476 current_version,
477 preferences,
478 }
479 }
480
481 pub fn negotiate_version(
483 &self,
484 peer_versions: &[ProtocolVersion],
485 peer_preferred: &ProtocolVersion,
486 ) -> Result<ProtocolVersion, VersionError> {
487 let mut available_versions = peer_versions.to_vec();
489 if !available_versions.contains(peer_preferred) {
490 available_versions.push(peer_preferred.clone());
491 }
492
493 if let Some(best_version) = self
495 .registry
496 .find_best_compatible_version(&available_versions, &self.preferences)
497 {
498 Ok(best_version)
499 } else {
500 Err(VersionError::IncompatibleVersions {
501 local: self.current_version.clone(),
502 remote: peer_preferred.clone(),
503 })
504 }
505 }
506
507 pub fn migrate_message(
509 &self,
510 message: &Message,
511 from_version: &ProtocolVersion,
512 to_version: &ProtocolVersion,
513 ) -> Result<Message, VersionError> {
514 if from_version == to_version {
515 return Ok(message.clone());
516 }
517
518 if let Some(strategy) = self
519 .registry
520 .get_migration_strategy(from_version, to_version)
521 {
522 match strategy {
523 MigrationStrategy::Direct => Ok(message.clone()),
524 MigrationStrategy::Transform(transform_fn) => transform_fn(message),
525 MigrationStrategy::Custom(custom_fn) => custom_fn(message),
526 MigrationStrategy::NotSupported => Err(VersionError::MigrationFailed {
527 from: from_version.clone(),
528 to: to_version.clone(),
529 reason: "Migration not supported".to_string(),
530 }),
531 }
532 } else {
533 Err(VersionError::MigrationFailed {
534 from: from_version.clone(),
535 to: to_version.clone(),
536 reason: "No migration path found".to_string(),
537 })
538 }
539 }
540
541 pub fn is_feature_available(&self, feature: &str) -> bool {
543 self.registry
544 .is_feature_supported(&self.current_version, feature)
545 }
546
547 pub fn current_version(&self) -> &ProtocolVersion {
549 &self.current_version
550 }
551
552 pub fn registry(&self) -> &VersionRegistry {
554 &self.registry
555 }
556
557 pub fn set_preferences(&mut self, preferences: VersionPreferences) {
559 self.preferences = preferences;
560 }
561
562 pub fn preferences(&self) -> &VersionPreferences {
564 &self.preferences
565 }
566}
567
568impl Default for VersionRegistry {
569 fn default() -> Self {
570 Self::new()
571 }
572}
573
574impl fmt::Display for ProtocolVersion {
575 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
577 if !self.features.is_empty() {
578 write!(f, "+{}", self.features.join(","))?;
579 }
580 Ok(())
581 }
582}
583
584fn downgrade_1_1_to_1_0(message: &Message) -> Result<Message, VersionError> {
586 let mut migrated_message = message.clone();
587
588 migrated_message.version = ProtocolVersion {
590 major: 1,
591 minor: 0,
592 patch: 0,
593 features: vec![],
594 };
595
596 migrated_message.headers.remove("dark-address");
598 migrated_message.headers.remove("shadow-route");
599
600 match &message.msg_type {
602 MessageType::Anonymous(_) => {
603 migrated_message.msg_type =
605 MessageType::Routing(crate::message::RoutingMessageType::Direct);
606 }
607 _ => {
608 }
610 }
611
612 Ok(migrated_message)
613}
614
615#[cfg(test)]
616mod tests {
617 use super::*;
618
619 #[test]
620 fn test_version_compatibility() {
621 let registry = VersionRegistry::new();
622
623 let v1_0_0 = ProtocolVersion {
624 major: 1,
625 minor: 0,
626 patch: 0,
627 features: vec![],
628 };
629 let v1_1_0 = ProtocolVersion {
630 major: 1,
631 minor: 1,
632 patch: 0,
633 features: vec![],
634 };
635
636 assert!(registry.are_compatible(&v1_0_0, &v1_1_0));
637 assert!(registry.are_compatible(&v1_1_0, &v1_0_0));
638 }
639
640 #[test]
641 fn test_feature_support() {
642 let registry = VersionRegistry::new();
643
644 let v1_0_0 = ProtocolVersion {
645 major: 1,
646 minor: 0,
647 patch: 0,
648 features: vec![],
649 };
650 let v1_1_0 = ProtocolVersion {
651 major: 1,
652 minor: 1,
653 patch: 0,
654 features: vec![],
655 };
656
657 assert!(registry.is_feature_supported(&v1_0_0, "basic-messaging"));
658 assert!(registry.is_feature_supported(&v1_1_0, "dark-addressing"));
659 assert!(!registry.is_feature_supported(&v1_0_0, "dark-addressing"));
660 }
661
662 #[test]
663 fn test_version_negotiation() {
664 let manager = VersionManager::new(ProtocolVersion {
665 major: 1,
666 minor: 1,
667 patch: 0,
668 features: vec![],
669 });
670
671 let peer_versions = vec![
672 ProtocolVersion {
673 major: 1,
674 minor: 0,
675 patch: 0,
676 features: vec![],
677 },
678 ProtocolVersion {
679 major: 1,
680 minor: 1,
681 patch: 0,
682 features: vec![],
683 },
684 ];
685
686 let peer_preferred = ProtocolVersion {
687 major: 1,
688 minor: 1,
689 patch: 0,
690 features: vec![],
691 };
692
693 let negotiated = manager
694 .negotiate_version(&peer_versions, &peer_preferred)
695 .unwrap();
696 assert_eq!(negotiated, peer_preferred);
697 }
698}