qudag_protocol/
versioning.rs

1//! Protocol versioning system for backward compatibility.
2
3use serde::{Deserialize, Serialize};
4use std::collections::{HashMap, HashSet};
5use std::fmt;
6use thiserror::Error;
7
8use crate::message::{Message, MessageType, ProtocolVersion};
9
10/// Type alias for custom migration function
11pub type CustomMigrationFn = Box<dyn Fn(&Message) -> Result<Message, VersionError> + Send + Sync>;
12
13/// Versioning-related errors
14#[derive(Debug, Error)]
15pub enum VersionError {
16    /// Unsupported protocol version
17    #[error("Unsupported protocol version: {version:?}")]
18    UnsupportedVersion { version: ProtocolVersion },
19
20    /// Incompatible protocol versions
21    #[error("Incompatible protocol versions: {local:?} vs {remote:?}")]
22    IncompatibleVersions {
23        local: ProtocolVersion,
24        remote: ProtocolVersion,
25    },
26
27    /// Feature not available in version
28    #[error("Feature '{feature}' not available in version {version:?}")]
29    FeatureNotAvailable {
30        feature: String,
31        version: ProtocolVersion,
32    },
33
34    /// Migration failed
35    #[error("Migration from {from:?} to {to:?} failed: {reason}")]
36    MigrationFailed {
37        from: ProtocolVersion,
38        to: ProtocolVersion,
39        reason: String,
40    },
41
42    /// Serialization/deserialization error
43    #[error("Serialization error: {reason}")]
44    SerializationError { reason: String },
45}
46
47/// Protocol version registry
48#[derive(Debug, Clone)]
49pub struct VersionRegistry {
50    /// Supported versions
51    supported_versions: Vec<VersionInfo>,
52    /// Feature compatibility matrix
53    feature_matrix: HashMap<ProtocolVersion, HashSet<String>>,
54    /// Message type compatibility
55    message_compatibility: HashMap<ProtocolVersion, HashSet<MessageType>>,
56    /// Migration paths between versions
57    migration_paths: HashMap<(ProtocolVersion, ProtocolVersion), MigrationStrategy>,
58}
59
60/// Version information
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct VersionInfo {
63    /// Protocol version
64    pub version: ProtocolVersion,
65    /// Version name/codename
66    pub name: String,
67    /// Release date (ISO 8601 format)
68    pub release_date: String,
69    /// Supported features
70    pub features: Vec<String>,
71    /// Deprecated features
72    pub deprecated_features: Vec<String>,
73    /// Security requirements
74    pub security_requirements: SecurityRequirements,
75    /// Backward compatibility information
76    pub compatibility: CompatibilityInfo,
77}
78
79/// Security requirements for a protocol version
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct SecurityRequirements {
82    /// Minimum key sizes
83    pub min_key_sizes: HashMap<String, u32>,
84    /// Required cryptographic algorithms
85    pub required_algorithms: Vec<String>,
86    /// Forbidden algorithms (deprecated/insecure)
87    pub forbidden_algorithms: Vec<String>,
88    /// Quantum resistance requirements
89    pub quantum_resistant: bool,
90}
91
92/// Compatibility information
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct CompatibilityInfo {
95    /// Versions this version is compatible with
96    pub compatible_with: Vec<ProtocolVersion>,
97    /// Minimum supported version for communication
98    pub min_supported_version: ProtocolVersion,
99    /// Breaking changes from previous version
100    pub breaking_changes: Vec<String>,
101    /// Migration notes
102    pub migration_notes: Vec<String>,
103}
104
105/// Migration strategy between versions
106pub enum MigrationStrategy {
107    /// Direct migration (no transformation needed)
108    Direct,
109    /// Transform messages using converter function
110    Transform(fn(&Message) -> Result<Message, VersionError>),
111    /// Custom migration logic
112    Custom(CustomMigrationFn),
113    /// Not supported
114    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                // Note: Custom closures cannot be cloned, so we return NotSupported
135                // This is a design limitation - if cloning is needed for Custom variants,
136                // consider using a different approach like an enum of migration types
137                MigrationStrategy::NotSupported
138            }
139            MigrationStrategy::NotSupported => MigrationStrategy::NotSupported,
140        }
141    }
142}
143
144/// Protocol version manager
145pub struct VersionManager {
146    /// Version registry
147    registry: VersionRegistry,
148    /// Current protocol version
149    current_version: ProtocolVersion,
150    /// Version negotiation preferences
151    preferences: VersionPreferences,
152}
153
154/// Version negotiation preferences
155#[derive(Debug, Clone)]
156pub struct VersionPreferences {
157    /// Preferred version for new connections
158    pub preferred_version: ProtocolVersion,
159    /// Minimum acceptable version
160    pub min_version: ProtocolVersion,
161    /// Maximum acceptable version
162    pub max_version: ProtocolVersion,
163    /// Whether to allow downgrade for compatibility
164    pub allow_downgrade: bool,
165    /// Features that must be supported
166    pub required_features: Vec<String>,
167    /// Features that are preferred but not required
168    pub preferred_features: Vec<String>,
169}
170
171impl VersionRegistry {
172    /// Create a new version registry with default versions
173    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        // Register default versions
182        registry.register_default_versions();
183        registry
184    }
185
186    /// Register default protocol versions
187    fn register_default_versions(&mut self) {
188        // Version 1.0.0 - Initial release
189        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        // Version 1.1.0 - Dark addressing support
235        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        // Set up migration paths
285        self.setup_migration_paths();
286    }
287
288    /// Register a new protocol version
289    pub fn register_version(&mut self, version_info: VersionInfo) {
290        let version = version_info.version.clone();
291
292        // Add to supported versions
293        self.supported_versions.push(version_info.clone());
294
295        // Update feature matrix
296        let features: HashSet<String> = version_info.features.into_iter().collect();
297        self.feature_matrix.insert(version.clone(), features);
298
299        // Update message compatibility (all message types supported by default)
300        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    /// Set up migration paths between versions
316    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        // Direct migration from 1.0.0 to 1.1.0 (backward compatible)
331        self.migration_paths
332            .insert((v1_0_0.clone(), v1_1_0.clone()), MigrationStrategy::Direct);
333
334        // Direct migration from 1.1.0 to 1.0.0 (downgrade, remove new features)
335        self.migration_paths.insert(
336            (v1_1_0, v1_0_0),
337            MigrationStrategy::Transform(downgrade_1_1_to_1_0),
338        );
339    }
340
341    /// Check if a version is supported
342    pub fn is_supported(&self, version: &ProtocolVersion) -> bool {
343        self.supported_versions
344            .iter()
345            .any(|v| &v.version == version)
346    }
347
348    /// Get version information
349    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    /// Get all supported versions
356    pub fn get_supported_versions(&self) -> &[VersionInfo] {
357        &self.supported_versions
358    }
359
360    /// Check if a feature is supported in a version
361    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    /// Check if two versions are compatible
369    pub fn are_compatible(&self, v1: &ProtocolVersion, v2: &ProtocolVersion) -> bool {
370        // Same version is always compatible
371        if v1 == v2 {
372            return true;
373        }
374
375        // Check compatibility information
376        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        // Check if versions follow semantic versioning compatibility
389        v1.is_compatible(v2)
390    }
391
392    /// Get migration strategy between versions
393    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    /// Find best compatible version
402    pub fn find_best_compatible_version(
403        &self,
404        available_versions: &[ProtocolVersion],
405        preferences: &VersionPreferences,
406    ) -> Option<ProtocolVersion> {
407        // Filter compatible versions
408        let mut compatible: Vec<&ProtocolVersion> = available_versions
409            .iter()
410            .filter(|v| {
411                // Check if version is in acceptable range
412                v.major >= preferences.min_version.major &&
413                v.major <= preferences.max_version.major &&
414                // Check if version is supported
415                self.is_supported(v) &&
416                // Check required features
417                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        // Sort by preference: preferred version first, then highest version
428        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                // Compare by version (highest first)
435                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    /// Create a new version manager
448    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    /// Negotiate protocol version with peer
482    pub fn negotiate_version(
483        &self,
484        peer_versions: &[ProtocolVersion],
485        peer_preferred: &ProtocolVersion,
486    ) -> Result<ProtocolVersion, VersionError> {
487        // Create combined list of versions to consider
488        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        // Find best compatible version
494        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    /// Migrate message between protocol versions
508    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    /// Check if a feature is available in current version
542    pub fn is_feature_available(&self, feature: &str) -> bool {
543        self.registry
544            .is_feature_supported(&self.current_version, feature)
545    }
546
547    /// Get current version
548    pub fn current_version(&self) -> &ProtocolVersion {
549        &self.current_version
550    }
551
552    /// Get version registry
553    pub fn registry(&self) -> &VersionRegistry {
554        &self.registry
555    }
556
557    /// Update preferences
558    pub fn set_preferences(&mut self, preferences: VersionPreferences) {
559        self.preferences = preferences;
560    }
561
562    /// Get preferences
563    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
584/// Migration function from version 1.1.0 to 1.0.0 (downgrade)
585fn downgrade_1_1_to_1_0(message: &Message) -> Result<Message, VersionError> {
586    let mut migrated_message = message.clone();
587
588    // Update version
589    migrated_message.version = ProtocolVersion {
590        major: 1,
591        minor: 0,
592        patch: 0,
593        features: vec![],
594    };
595
596    // Remove headers that are specific to 1.1.0
597    migrated_message.headers.remove("dark-address");
598    migrated_message.headers.remove("shadow-route");
599
600    // For certain message types, we might need to modify the payload
601    match &message.msg_type {
602        MessageType::Anonymous(_) => {
603            // Convert anonymous messages to regular routing messages for 1.0.0
604            migrated_message.msg_type =
605                MessageType::Routing(crate::message::RoutingMessageType::Direct);
606        }
607        _ => {
608            // Most message types are compatible
609        }
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}