diff --git a/Cargo.lock b/Cargo.lock index 91b7e1a96..fe3173e32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -855,6 +855,7 @@ dependencies = [ "regex", "report", "schemars", + "semver 1.0.23", "serde", "serde_json", "serde_with 3.11.0", @@ -1703,6 +1704,7 @@ dependencies = [ "multisig", "report", "router-api", + "semver 1.0.23", "service-registry-api", "thiserror", "tofn", diff --git a/Cargo.toml b/Cargo.toml index f8b73745e..94af7cf71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ rewards = { version = "^1.2.0", path = "contracts/rewards" } router = { version = "^1.1.0", path = "contracts/router" } router-api = { version = "^1.0.0", path = "packages/router-api" } schemars = "0.8.10" +semver = "1.0" serde = { version = "1.0.145", default-features = false, features = ["derive"] } serde_json = "1.0.89" service-registry = { version = "^1.1.0", path = "contracts/service-registry" } diff --git a/contracts/coordinator/Cargo.toml b/contracts/coordinator/Cargo.toml index da5220e94..fa49e44bd 100644 --- a/contracts/coordinator/Cargo.toml +++ b/contracts/coordinator/Cargo.toml @@ -44,6 +44,7 @@ msgs-derive = { workspace = true } multisig = { workspace = true, features = ["library"] } report = { workspace = true } router-api = { workspace = true } +semver = { workspace = true } service-registry-api = { workspace = true } thiserror = { workspace = true } diff --git a/contracts/coordinator/src/contract.rs b/contracts/coordinator/src/contract.rs index 71f3573cf..fa7cd8a7b 100644 --- a/contracts/coordinator/src/contract.rs +++ b/contracts/coordinator/src/contract.rs @@ -7,13 +7,14 @@ use axelar_wasm_std::{address, permission_control, FnExt}; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; use cosmwasm_std::{ - to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, Storage, + to_json_binary, Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, Storage }; use error_stack::{report, ResultExt}; use itertools::Itertools; +use semver::{Version, VersionReq}; use crate::error::ContractError; -use crate::msg::{ExecuteMsg, InstantiateMsg, MigrationMsg, QueryMsg}; +use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{is_prover_registered, Config, CONFIG}; pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); @@ -23,12 +24,11 @@ pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub fn migrate( deps: DepsMut, _env: Env, - msg: MigrationMsg, + _msg: Empty, ) -> Result { - let service_registry = validate_cosmwasm_address(deps.api, &msg.service_registry)?; - - migrations::v1_0_0::migrate(deps.storage, service_registry) - .change_context(ContractError::Migration)?; + let old_version = Version::parse(&cw2::get_contract_version(deps.storage)?.version)?; + let version_requirement = VersionReq::parse(">= 1.1.0, < 1.2.0")?; + assert!(version_requirement.matches(&old_version)); // this needs to be the last thing to do during migration, // because previous migration steps should check the old version @@ -304,4 +304,16 @@ mod tests { .to_string() )); } + + #[test] + fn migrate_sets_contract_version() { + let governance = "governance_for_coordinator"; + let mut test_setup = setup(governance); + + migrate(test_setup.deps.as_mut(), mock_env(), Empty {}).unwrap(); + + let contract_version = cw2::get_contract_version(test_setup.deps.as_mut().storage).unwrap(); + assert_eq!(contract_version.contract, CONTRACT_NAME); + assert_eq!(contract_version.version, CONTRACT_VERSION); + } } diff --git a/contracts/coordinator/src/contract/migrations/mod.rs b/contracts/coordinator/src/contract/migrations/mod.rs index 1d185e9d7..e69de29bb 100644 --- a/contracts/coordinator/src/contract/migrations/mod.rs +++ b/contracts/coordinator/src/contract/migrations/mod.rs @@ -1 +0,0 @@ -pub mod v1_0_0; diff --git a/contracts/coordinator/src/contract/migrations/v1_0_0.rs b/contracts/coordinator/src/contract/migrations/v1_0_0.rs deleted file mode 100644 index c89703ee8..000000000 --- a/contracts/coordinator/src/contract/migrations/v1_0_0.rs +++ /dev/null @@ -1,115 +0,0 @@ -#![allow(deprecated)] - -use axelar_wasm_std::nonempty; -use cosmwasm_std::{Addr, StdError, Storage}; -use cw2::VersionError; - -use crate::contract::CONTRACT_NAME; - -const BASE_VERSION: &str = "1.0.0"; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Std(#[from] StdError), - #[error(transparent)] - Version(#[from] VersionError), - #[error(transparent)] - NonEmpty(#[from] nonempty::Error), -} - -pub fn migrate(storage: &mut dyn Storage, service_registry: Addr) -> Result<(), Error> { - cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; - - migrate_config(storage, service_registry)?; - Ok(()) -} - -fn migrate_config(storage: &mut dyn Storage, service_registry: Addr) -> Result<(), Error> { - let new_config = crate::state::Config { service_registry }; - crate::state::CONFIG.save(storage, &new_config)?; - Ok(()) -} - -#[cfg(test)] -mod tests { - use axelar_wasm_std::{address, permission_control}; - use cosmwasm_schema::cw_serde; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response}; - - use crate::contract::migrations::v1_0_0::{self, BASE_VERSION}; - use crate::contract::CONTRACT_NAME; - - const GOVERNANCE: &str = "governance"; - - #[test] - fn migrate_checks_contract_version() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); - - assert!(v1_0_0::migrate(deps.as_mut().storage, Addr::unchecked("random_service")).is_err()); - - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, BASE_VERSION).unwrap(); - - assert!(v1_0_0::migrate(deps.as_mut().storage, Addr::unchecked("random_service")).is_ok()); - } - - #[test] - fn migrate_config() { - let mut deps = mock_dependencies(); - instantiate( - deps.as_mut(), - mock_env(), - mock_info("admin", &[]), - InstantiateMsg { - governance_address: "governance".to_string(), - }, - ) - .unwrap(); - - let service_registry_address = Addr::unchecked("random_service"); - assert!(v1_0_0::migrate(deps.as_mut().storage, service_registry_address.clone()).is_ok()); - - let config_result = crate::state::CONFIG.load(deps.as_mut().storage); - assert!(config_result.is_ok()); - - let config = config_result.unwrap(); - assert_eq!(config.service_registry, service_registry_address) - } - - fn instantiate_contract(deps: DepsMut) { - instantiate( - deps, - mock_env(), - mock_info("admin", &[]), - InstantiateMsg { - governance_address: GOVERNANCE.to_string(), - }, - ) - .unwrap(); - } - - #[deprecated(since = "1.0.0", note = "only used to test the migration")] - fn instantiate( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?; - - let governance = address::validate_cosmwasm_address(deps.api, &msg.governance_address)?; - permission_control::set_governance(deps.storage, &governance)?; - - Ok(Response::default()) - } - - #[cw_serde] - #[deprecated(since = "1.0.0", note = "only used to test the migration")] - struct InstantiateMsg { - pub governance_address: String, - } -} diff --git a/contracts/coordinator/src/msg.rs b/contracts/coordinator/src/msg.rs index e93bc6ad5..e1c3b5f4c 100644 --- a/contracts/coordinator/src/msg.rs +++ b/contracts/coordinator/src/msg.rs @@ -7,11 +7,6 @@ use msgs_derive::EnsurePermissions; use router_api::ChainName; use service_registry_api::Verifier; -#[cw_serde] -pub struct MigrationMsg { - pub service_registry: String, -} - #[cw_serde] pub struct InstantiateMsg { pub governance_address: String, diff --git a/packages/axelar-wasm-std/Cargo.toml b/packages/axelar-wasm-std/Cargo.toml index 3472b6de8..8395706cf 100644 --- a/packages/axelar-wasm-std/Cargo.toml +++ b/packages/axelar-wasm-std/Cargo.toml @@ -43,6 +43,7 @@ num-traits = { workspace = true } regex = { version = "1.10.0", default-features = false, features = ["perf", "std"] } report = { workspace = true } schemars = "0.8.10" +semver = { workspace = true } serde = { version = "1.0.145", default-features = false, features = ["derive"] } serde_json = "1.0.89" serde_with = { version = "3.11.0", features = ["macros"] } diff --git a/packages/axelar-wasm-std/src/error.rs b/packages/axelar-wasm-std/src/error.rs index d08977ca8..7e161f893 100644 --- a/packages/axelar-wasm-std/src/error.rs +++ b/packages/axelar-wasm-std/src/error.rs @@ -39,6 +39,14 @@ impl From for ContractError { } } +impl From for ContractError { + fn from(err: semver::Error) -> Self { + ContractError { + report: report!(err).change_context(Error::Report), + } + } +} + impl From for ContractError { fn from(err: permission_control::Error) -> Self { ContractError {