1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use cosmwasm_std::{StdError, StdResult, Storage};
use cw2::{get_contract_version, set_contract_version};
use semver::Version;
pub fn ensure_from_older_version(
storage: &mut dyn Storage,
name: &str,
new_version: &str,
) -> StdResult<Version> {
let version: Version = new_version.parse().map_err(from_semver)?;
let stored = get_contract_version(storage)?;
let storage_version: Version = stored.version.parse().map_err(from_semver)?;
if name != stored.contract {
let msg = format!("Cannot migrate from {} to {}", stored.contract, name);
return Err(StdError::generic_err(msg));
}
if storage_version > version {
let msg = format!(
"Cannot migrate from newer version ({}) to older ({})",
stored.version, new_version
);
return Err(StdError::generic_err(msg));
}
if storage_version < version {
set_contract_version(storage, name, new_version)?;
}
Ok(storage_version)
}
fn from_semver(err: semver::Error) -> StdError {
StdError::generic_err(format!("Semver: {}", err))
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::MockStorage;
#[test]
fn accepts_identical_version() {
let mut storage = MockStorage::new();
set_contract_version(&mut storage, "demo", "0.1.2").unwrap();
ensure_from_older_version(&mut storage, "demo", "0.1.2").unwrap();
}
#[test]
fn accepts_and_updates_on_newer_version() {
let mut storage = MockStorage::new();
set_contract_version(&mut storage, "demo", "0.4.0").unwrap();
let original_version = ensure_from_older_version(&mut storage, "demo", "0.4.2").unwrap();
assert_eq!(original_version.to_string(), "0.4.0".to_string());
let stored = get_contract_version(&storage).unwrap();
assert_eq!(stored.contract, "demo".to_string());
assert_eq!(stored.version, "0.4.2".to_string());
}
#[test]
fn errors_on_name_mismatch() {
let mut storage = MockStorage::new();
set_contract_version(&mut storage, "demo", "0.1.2").unwrap();
let err = ensure_from_older_version(&mut storage, "cw20-base", "0.1.2").unwrap_err();
assert!(err.to_string().contains("cw20-base"), "{}", err);
assert!(err.to_string().contains("demo"), "{}", err);
}
#[test]
fn errors_on_older_version() {
let mut storage = MockStorage::new();
set_contract_version(&mut storage, "demo", "0.10.2").unwrap();
let err = ensure_from_older_version(&mut storage, "demo", "0.9.7").unwrap_err();
assert!(err.to_string().contains("0.10.2"), "{}", err);
assert!(err.to_string().contains("0.9.7"), "{}", err);
}
#[test]
fn errors_on_broken_version() {
let mut storage = MockStorage::new();
let err = ensure_from_older_version(&mut storage, "demo", "0.a.7").unwrap_err();
assert!(
err.to_string().contains("unexpected character 'a'"),
"{}",
err
);
}
}