casper_types/
protocol_version.rs

1use alloc::{format, string::String, vec::Vec};
2use core::{convert::TryFrom, fmt, str::FromStr};
3
4#[cfg(feature = "datasize")]
5use datasize::DataSize;
6#[cfg(feature = "json-schema")]
7use schemars::JsonSchema;
8use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
9
10use crate::{
11    bytesrepr::{Error, FromBytes, ToBytes},
12    ParseSemVerError, SemVer,
13};
14
15/// A newtype wrapping a [`SemVer`] which represents a Casper Platform protocol version.
16#[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
17#[cfg_attr(feature = "datasize", derive(DataSize))]
18pub struct ProtocolVersion(SemVer);
19
20/// The result of [`ProtocolVersion::check_next_version`].
21#[derive(Debug, PartialEq, Eq)]
22pub enum VersionCheckResult {
23    /// Upgrade possible.
24    Valid {
25        /// Is this a major protocol version upgrade?
26        is_major_version: bool,
27    },
28    /// Upgrade is invalid.
29    Invalid,
30}
31
32impl VersionCheckResult {
33    /// Checks if given version result is invalid.
34    ///
35    /// Invalid means that a given version can not be followed.
36    pub fn is_invalid(&self) -> bool {
37        matches!(self, VersionCheckResult::Invalid)
38    }
39
40    /// Checks if given version is a major protocol version upgrade.
41    pub fn is_major_version(&self) -> bool {
42        match self {
43            VersionCheckResult::Valid { is_major_version } => *is_major_version,
44            VersionCheckResult::Invalid => false,
45        }
46    }
47}
48
49impl ProtocolVersion {
50    /// Version 1.0.0.
51    pub const V1_0_0: ProtocolVersion = ProtocolVersion(SemVer {
52        major: 1,
53        minor: 0,
54        patch: 0,
55    });
56
57    /// Constructs a new `ProtocolVersion` from `version`.
58    pub const fn new(version: SemVer) -> ProtocolVersion {
59        ProtocolVersion(version)
60    }
61
62    /// Constructs a new `ProtocolVersion` from the given semver parts.
63    pub const fn from_parts(major: u32, minor: u32, patch: u32) -> ProtocolVersion {
64        let sem_ver = SemVer::new(major, minor, patch);
65        Self::new(sem_ver)
66    }
67
68    /// Returns the inner [`SemVer`].
69    pub fn value(&self) -> SemVer {
70        self.0
71    }
72
73    /// Checks if next version can be followed.
74    pub fn check_next_version(&self, next: &ProtocolVersion) -> VersionCheckResult {
75        // Protocol major versions should increase monotonically by 1.
76        let major_bumped = self.0.major.saturating_add(1);
77        if next.0.major < self.0.major || next.0.major > major_bumped {
78            return VersionCheckResult::Invalid;
79        }
80
81        if next.0.major == major_bumped {
82            return VersionCheckResult::Valid {
83                is_major_version: true,
84            };
85        }
86
87        // Covers the equal major versions
88        debug_assert_eq!(next.0.major, self.0.major);
89
90        if next.0.minor < self.0.minor {
91            // Protocol minor versions within the same major version should not go backwards.
92            return VersionCheckResult::Invalid;
93        }
94
95        if next.0.minor > self.0.minor {
96            return VersionCheckResult::Valid {
97                is_major_version: false,
98            };
99        }
100
101        // Code belows covers equal minor versions
102        debug_assert_eq!(next.0.minor, self.0.minor);
103
104        // Protocol patch versions should increase monotonically but can be skipped.
105        if next.0.patch <= self.0.patch {
106            return VersionCheckResult::Invalid;
107        }
108
109        VersionCheckResult::Valid {
110            is_major_version: false,
111        }
112    }
113
114    /// Checks if given protocol version is compatible with current one.
115    ///
116    /// Two protocol versions with different major version are considered to be incompatible.
117    pub fn is_compatible_with(&self, version: &ProtocolVersion) -> bool {
118        self.0.major == version.0.major
119    }
120}
121
122impl ToBytes for ProtocolVersion {
123    fn to_bytes(&self) -> Result<Vec<u8>, Error> {
124        self.value().to_bytes()
125    }
126
127    fn serialized_length(&self) -> usize {
128        self.value().serialized_length()
129    }
130
131    fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), Error> {
132        writer.extend(self.0.major.to_le_bytes());
133        writer.extend(self.0.minor.to_le_bytes());
134        writer.extend(self.0.patch.to_le_bytes());
135        Ok(())
136    }
137}
138
139impl FromBytes for ProtocolVersion {
140    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
141        let (version, rem) = SemVer::from_bytes(bytes)?;
142        let protocol_version = ProtocolVersion::new(version);
143        Ok((protocol_version, rem))
144    }
145}
146
147impl FromStr for ProtocolVersion {
148    type Err = ParseSemVerError;
149
150    fn from_str(s: &str) -> Result<Self, ParseSemVerError> {
151        let version = SemVer::try_from(s)?;
152        Ok(ProtocolVersion::new(version))
153    }
154}
155
156impl Serialize for ProtocolVersion {
157    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
158        if serializer.is_human_readable() {
159            let str = format!("{}.{}.{}", self.0.major, self.0.minor, self.0.patch);
160            String::serialize(&str, serializer)
161        } else {
162            self.0.serialize(serializer)
163        }
164    }
165}
166
167impl<'de> Deserialize<'de> for ProtocolVersion {
168    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
169        let semver = if deserializer.is_human_readable() {
170            let value_as_string = String::deserialize(deserializer)?;
171            SemVer::try_from(value_as_string.as_str()).map_err(SerdeError::custom)?
172        } else {
173            SemVer::deserialize(deserializer)?
174        };
175        Ok(ProtocolVersion(semver))
176    }
177}
178
179#[cfg(feature = "json-schema")]
180impl JsonSchema for ProtocolVersion {
181    fn schema_name() -> String {
182        String::from("ProtocolVersion")
183    }
184
185    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
186        let schema = gen.subschema_for::<String>();
187        let mut schema_object = schema.into_object();
188        schema_object.metadata().description = Some("Casper Platform protocol version".to_string());
189        schema_object.into()
190    }
191}
192
193impl fmt::Display for ProtocolVersion {
194    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195        self.0.fmt(f)
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202    use crate::SemVer;
203
204    #[test]
205    fn should_follow_version_with_optional_code() {
206        let value = VersionCheckResult::Valid {
207            is_major_version: false,
208        };
209        assert!(!value.is_invalid());
210        assert!(!value.is_major_version());
211    }
212
213    #[test]
214    fn should_follow_version_with_required_code() {
215        let value = VersionCheckResult::Valid {
216            is_major_version: true,
217        };
218        assert!(!value.is_invalid());
219        assert!(value.is_major_version());
220    }
221
222    #[test]
223    fn should_not_follow_version_with_invalid_code() {
224        let value = VersionCheckResult::Invalid;
225        assert!(value.is_invalid());
226        assert!(!value.is_major_version());
227    }
228
229    #[test]
230    fn should_be_able_to_get_instance() {
231        let initial_value = SemVer::new(1, 0, 0);
232        let item = ProtocolVersion::new(initial_value);
233        assert_eq!(initial_value, item.value(), "should have equal value")
234    }
235
236    #[test]
237    fn should_be_able_to_compare_two_instances() {
238        let lhs = ProtocolVersion::new(SemVer::new(1, 0, 0));
239        let rhs = ProtocolVersion::new(SemVer::new(1, 0, 0));
240        assert_eq!(lhs, rhs, "should be equal");
241        let rhs = ProtocolVersion::new(SemVer::new(2, 0, 0));
242        assert_ne!(lhs, rhs, "should not be equal")
243    }
244
245    #[test]
246    fn should_be_able_to_default() {
247        let defaulted = ProtocolVersion::default();
248        let expected = ProtocolVersion::new(SemVer::new(0, 0, 0));
249        assert_eq!(defaulted, expected, "should be equal")
250    }
251
252    #[test]
253    fn should_be_able_to_compare_relative_value() {
254        let lhs = ProtocolVersion::new(SemVer::new(2, 0, 0));
255        let rhs = ProtocolVersion::new(SemVer::new(1, 0, 0));
256        assert!(lhs > rhs, "should be gt");
257        let rhs = ProtocolVersion::new(SemVer::new(2, 0, 0));
258        assert!(lhs >= rhs, "should be gte");
259        assert!(lhs <= rhs, "should be lte");
260        let lhs = ProtocolVersion::new(SemVer::new(1, 0, 0));
261        assert!(lhs < rhs, "should be lt");
262    }
263
264    #[test]
265    fn should_follow_major_version_upgrade() {
266        // If the upgrade protocol version is lower than or the same as EE's current in-use protocol
267        // version the upgrade is rejected and an error is returned; this includes the special case
268        // of a defaulted protocol version ( 0.0.0 ).
269        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
270        let next = ProtocolVersion::new(SemVer::new(2, 0, 0));
271        assert!(
272            prev.check_next_version(&next).is_major_version(),
273            "should be major version"
274        );
275    }
276
277    #[test]
278    fn should_reject_if_major_version_decreases() {
279        let prev = ProtocolVersion::new(SemVer::new(10, 0, 0));
280        let next = ProtocolVersion::new(SemVer::new(9, 0, 0));
281        // Major version must not decrease ...
282        assert_eq!(prev.check_next_version(&next), VersionCheckResult::Invalid);
283    }
284
285    #[test]
286    fn should_check_follows_minor_version_upgrade() {
287        // [major version] may remain the same in the case of a minor or patch version increase.
288
289        // Minor version must not decrease within the same major version
290        let prev = ProtocolVersion::new(SemVer::new(1, 1, 0));
291        let next = ProtocolVersion::new(SemVer::new(1, 2, 0));
292
293        let value = prev.check_next_version(&next);
294        assert!(!value.is_invalid(), "should be valid");
295        assert!(!value.is_major_version(), "should not be a major version");
296    }
297
298    #[test]
299    fn should_not_care_if_minor_bump_resets_patch() {
300        let prev = ProtocolVersion::new(SemVer::new(1, 2, 0));
301        let next = ProtocolVersion::new(SemVer::new(1, 3, 1));
302        assert_eq!(
303            prev.check_next_version(&next),
304            VersionCheckResult::Valid {
305                is_major_version: false
306            }
307        );
308
309        let prev = ProtocolVersion::new(SemVer::new(1, 20, 42));
310        let next = ProtocolVersion::new(SemVer::new(1, 30, 43));
311        assert_eq!(
312            prev.check_next_version(&next),
313            VersionCheckResult::Valid {
314                is_major_version: false
315            }
316        );
317    }
318
319    #[test]
320    fn should_not_care_if_major_bump_resets_minor_or_patch() {
321        // A major version increase resets both the minor and patch versions to ( 0.0 ).
322        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
323        let next = ProtocolVersion::new(SemVer::new(2, 1, 0));
324        assert_eq!(
325            prev.check_next_version(&next),
326            VersionCheckResult::Valid {
327                is_major_version: true
328            }
329        );
330
331        let next = ProtocolVersion::new(SemVer::new(2, 0, 1));
332        assert_eq!(
333            prev.check_next_version(&next),
334            VersionCheckResult::Valid {
335                is_major_version: true
336            }
337        );
338
339        let next = ProtocolVersion::new(SemVer::new(2, 1, 1));
340        assert_eq!(
341            prev.check_next_version(&next),
342            VersionCheckResult::Valid {
343                is_major_version: true
344            }
345        );
346    }
347
348    #[test]
349    fn should_reject_patch_version_rollback() {
350        // Patch version must not decrease or remain the same within the same major and minor
351        // version pair, but may skip.
352        let prev = ProtocolVersion::new(SemVer::new(1, 0, 42));
353        let next = ProtocolVersion::new(SemVer::new(1, 0, 41));
354        assert_eq!(prev.check_next_version(&next), VersionCheckResult::Invalid);
355        let next = ProtocolVersion::new(SemVer::new(1, 0, 13));
356        assert_eq!(prev.check_next_version(&next), VersionCheckResult::Invalid);
357    }
358
359    #[test]
360    fn should_accept_patch_version_update_with_optional_code() {
361        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
362        let next = ProtocolVersion::new(SemVer::new(1, 0, 1));
363        let value = prev.check_next_version(&next);
364        assert!(!value.is_invalid(), "should be valid");
365        assert!(!value.is_major_version(), "should not be a major version");
366
367        let prev = ProtocolVersion::new(SemVer::new(1, 0, 8));
368        let next = ProtocolVersion::new(SemVer::new(1, 0, 42));
369        let value = prev.check_next_version(&next);
370        assert!(!value.is_invalid(), "should be valid");
371        assert!(!value.is_major_version(), "should not be a major version");
372    }
373
374    #[test]
375    fn should_accept_minor_version_update_with_optional_code() {
376        // installer is optional for minor bump
377        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
378        let next = ProtocolVersion::new(SemVer::new(1, 1, 0));
379        let value = prev.check_next_version(&next);
380        assert!(!value.is_invalid(), "should be valid");
381        assert!(!value.is_major_version(), "should not be a major version");
382
383        let prev = ProtocolVersion::new(SemVer::new(3, 98, 0));
384        let next = ProtocolVersion::new(SemVer::new(3, 99, 0));
385        let value = prev.check_next_version(&next);
386        assert!(!value.is_invalid(), "should be valid");
387        assert!(!value.is_major_version(), "should not be a major version");
388    }
389
390    #[test]
391    fn should_allow_skip_minor_version_within_major_version() {
392        let prev = ProtocolVersion::new(SemVer::new(1, 1, 0));
393
394        let next = ProtocolVersion::new(SemVer::new(1, 3, 0));
395        assert_eq!(
396            prev.check_next_version(&next),
397            VersionCheckResult::Valid {
398                is_major_version: false
399            }
400        );
401
402        let next = ProtocolVersion::new(SemVer::new(1, 7, 0));
403        assert_eq!(
404            prev.check_next_version(&next),
405            VersionCheckResult::Valid {
406                is_major_version: false
407            }
408        );
409    }
410
411    #[test]
412    fn should_allow_skip_patch_version_within_minor_version() {
413        let prev = ProtocolVersion::new(SemVer::new(1, 1, 0));
414
415        let next = ProtocolVersion::new(SemVer::new(1, 1, 2));
416        assert_eq!(
417            prev.check_next_version(&next),
418            VersionCheckResult::Valid {
419                is_major_version: false
420            }
421        );
422    }
423
424    #[test]
425    fn should_allow_skipped_minor_and_patch_on_major_bump() {
426        // skip minor
427        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
428        let next = ProtocolVersion::new(SemVer::new(2, 1, 0));
429        assert_eq!(
430            prev.check_next_version(&next),
431            VersionCheckResult::Valid {
432                is_major_version: true
433            }
434        );
435
436        // skip patch
437        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
438        let next = ProtocolVersion::new(SemVer::new(2, 0, 1));
439        assert_eq!(
440            prev.check_next_version(&next),
441            VersionCheckResult::Valid {
442                is_major_version: true
443            }
444        );
445
446        // skip many minors and patches
447        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
448        let next = ProtocolVersion::new(SemVer::new(2, 3, 10));
449        assert_eq!(
450            prev.check_next_version(&next),
451            VersionCheckResult::Valid {
452                is_major_version: true
453            }
454        );
455    }
456
457    #[test]
458    fn should_allow_code_on_major_update() {
459        // major upgrade requires installer to be present
460        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
461        let next = ProtocolVersion::new(SemVer::new(2, 0, 0));
462        assert!(
463            prev.check_next_version(&next).is_major_version(),
464            "should be major version"
465        );
466
467        let prev = ProtocolVersion::new(SemVer::new(2, 99, 99));
468        let next = ProtocolVersion::new(SemVer::new(3, 0, 0));
469        assert!(
470            prev.check_next_version(&next).is_major_version(),
471            "should be major version"
472        );
473    }
474
475    #[test]
476    fn should_not_skip_major_version() {
477        // can bump only by 1
478        let prev = ProtocolVersion::new(SemVer::new(1, 0, 0));
479        let next = ProtocolVersion::new(SemVer::new(3, 0, 0));
480        assert_eq!(prev.check_next_version(&next), VersionCheckResult::Invalid);
481    }
482
483    #[test]
484    fn should_reject_major_version_rollback() {
485        // can bump forward
486        let prev = ProtocolVersion::new(SemVer::new(2, 0, 0));
487        let next = ProtocolVersion::new(SemVer::new(0, 0, 0));
488        assert_eq!(prev.check_next_version(&next), VersionCheckResult::Invalid);
489    }
490
491    #[test]
492    fn should_check_same_version_is_invalid() {
493        for ver in &[
494            ProtocolVersion::from_parts(1, 0, 0),
495            ProtocolVersion::from_parts(1, 2, 0),
496            ProtocolVersion::from_parts(1, 2, 3),
497        ] {
498            assert_eq!(ver.check_next_version(ver), VersionCheckResult::Invalid);
499        }
500    }
501
502    #[test]
503    fn should_not_be_compatible_with_different_major_version() {
504        let current = ProtocolVersion::from_parts(1, 2, 3);
505        let other = ProtocolVersion::from_parts(2, 5, 6);
506        assert!(!current.is_compatible_with(&other));
507
508        let current = ProtocolVersion::from_parts(1, 0, 0);
509        let other = ProtocolVersion::from_parts(2, 0, 0);
510        assert!(!current.is_compatible_with(&other));
511    }
512
513    #[test]
514    fn should_be_compatible_with_equal_major_version_backwards() {
515        let current = ProtocolVersion::from_parts(1, 99, 99);
516        let other = ProtocolVersion::from_parts(1, 0, 0);
517        assert!(current.is_compatible_with(&other));
518    }
519
520    #[test]
521    fn should_be_compatible_with_equal_major_version_forwards() {
522        let current = ProtocolVersion::from_parts(1, 0, 0);
523        let other = ProtocolVersion::from_parts(1, 99, 99);
524        assert!(current.is_compatible_with(&other));
525    }
526
527    #[test]
528    fn should_serialize_to_json_properly() {
529        let protocol_version = ProtocolVersion::from_parts(1, 1, 1);
530        let json = serde_json::to_string(&protocol_version).unwrap();
531        let expected = "\"1.1.1\"";
532        assert_eq!(json, expected);
533    }
534
535    #[test]
536    fn serialize_roundtrip() {
537        let protocol_version = ProtocolVersion::from_parts(1, 1, 1);
538        let serialized_json = serde_json::to_string(&protocol_version).unwrap();
539        assert_eq!(
540            protocol_version,
541            serde_json::from_str(&serialized_json).unwrap()
542        );
543
544        let serialized_bincode = bincode::serialize(&protocol_version).unwrap();
545        assert_eq!(
546            protocol_version,
547            bincode::deserialize(&serialized_bincode).unwrap()
548        );
549    }
550}