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#[derive(Copy, Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
17#[cfg_attr(feature = "datasize", derive(DataSize))]
18pub struct ProtocolVersion(SemVer);
19
20#[derive(Debug, PartialEq, Eq)]
22pub enum VersionCheckResult {
23 Valid {
25 is_major_version: bool,
27 },
28 Invalid,
30}
31
32impl VersionCheckResult {
33 pub fn is_invalid(&self) -> bool {
37 matches!(self, VersionCheckResult::Invalid)
38 }
39
40 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 pub const V1_0_0: ProtocolVersion = ProtocolVersion(SemVer {
52 major: 1,
53 minor: 0,
54 patch: 0,
55 });
56
57 pub const fn new(version: SemVer) -> ProtocolVersion {
59 ProtocolVersion(version)
60 }
61
62 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 pub fn value(&self) -> SemVer {
70 self.0
71 }
72
73 pub fn check_next_version(&self, next: &ProtocolVersion) -> VersionCheckResult {
75 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 debug_assert_eq!(next.0.major, self.0.major);
89
90 if next.0.minor < self.0.minor {
91 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 debug_assert_eq!(next.0.minor, self.0.minor);
103
104 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 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 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 assert_eq!(prev.check_next_version(&next), VersionCheckResult::Invalid);
283 }
284
285 #[test]
286 fn should_check_follows_minor_version_upgrade() {
287 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 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 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 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 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 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 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 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 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 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}