From bef4d34cc674892725c36c5fcbb467aaa38c38c8 Mon Sep 17 00:00:00 2001 From: srdtrk <59252793+srdtrk@users.noreply.github.com> Date: Sun, 22 Oct 2023 18:17:51 +0300 Subject: [PATCH] feat: added `helpers.rs` and basic migration logic --- Cargo.lock | 7 +++ Cargo.toml | 1 + src/contract.rs | 38 +++++++++++- src/helpers.rs | 142 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/types/error.rs | 6 ++ src/types/msg.rs | 4 ++ 7 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 src/helpers.rs diff --git a/Cargo.lock b/Cargo.lock index 8d3ade61..1b65ed7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,6 +232,7 @@ dependencies = [ "cw-storage-plus", "cw2", "schemars", + "semver", "serde", "serde-json-wasm", "thiserror", @@ -648,6 +649,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + [[package]] name = "serde" version = "1.0.171" diff --git a/Cargo.toml b/Cargo.toml index eeddeb0f..e6af83d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"] } serde-json-wasm = "0.5.1" thiserror = { version = "1.0.31" } cosmos-sdk-proto = { version = "0.20.0", default-features = false } +semver = "1.0" [dev-dependencies] base64 = "0.13.1" diff --git a/src/contract.rs b/src/contract.rs index bcca8612..2fa66366 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -4,7 +4,7 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; -use crate::types::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; +use crate::types::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg}; use crate::types::state::{ CallbackCounter, ChannelState, ContractState, CALLBACK_COUNTER, CHANNEL_STATE, STATE, }; @@ -75,6 +75,18 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { } } +/// Migrate contract if version is lower than current version +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn migrate(deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result { + migrate::validate_semver(deps.as_ref())?; + + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + // If state structure changed in any contract version in the way migration is needed, it + // should occur here + + Ok(Response::default()) +} + mod execute { use cosmwasm_std::coins; @@ -168,6 +180,30 @@ mod query { } } +mod migrate { + use super::*; + + pub fn validate_semver(deps: Deps) -> Result<(), ContractError> { + let prev_cw2_version = cw2::get_contract_version(deps.storage)?; + if prev_cw2_version.contract != CONTRACT_NAME { + return Err(ContractError::InvalidMigrationVersion { + expected: CONTRACT_NAME.to_string(), + actual: prev_cw2_version.contract, + }); + } + + let version: semver::Version = CONTRACT_VERSION.parse()?; + let prev_version: semver::Version = prev_cw2_version.version.parse()?; + if prev_version >= version { + return Err(ContractError::InvalidMigrationVersion { + expected: format!("> {}", prev_version), + actual: CONTRACT_VERSION.to_string(), + }); + } + Ok(()) + } +} + #[cfg(test)] mod tests { use crate::ibc::types::{metadata::TxEncoding, packet::IcaPacketData}; diff --git a/src/helpers.rs b/src/helpers.rs new file mode 100644 index 00000000..aac8a7ac --- /dev/null +++ b/src/helpers.rs @@ -0,0 +1,142 @@ +//! This file contains helper functions for working with this contract from +//! external contracts. + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use cosmwasm_std::{to_binary, Addr, Binary, CosmosMsg, QuerierWrapper, StdResult, WasmMsg}; + +use crate::types::{msg, state}; + +/// CwIcaControllerContract is a wrapper around Addr that provides helpers +/// for working with this contract. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct CwIcaControllerContract(pub Addr); + +/// CwIcaControllerCodeId is a wrapper around u64 that provides helpers for +/// initializing this contract. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)] +pub struct CwIcaControllerCode(pub u64); + +impl CwIcaControllerContract { + /// new creates a new [`CwIcaControllerContract`] + pub fn new(addr: Addr) -> Self { + Self(addr) + } + + /// addr returns the address of the contract + pub fn addr(&self) -> Addr { + self.0.clone() + } + + /// call creates a [`WasmMsg::Execute`] message targeting this contract, + pub fn call(&self, msg: impl Into) -> StdResult { + let msg = to_binary(&msg.into())?; + Ok(WasmMsg::Execute { + contract_addr: self.addr().into(), + msg, + funds: vec![], + } + .into()) + } + + /// query_channel queries the [`state::ChannelState`] of this contract + pub fn query_channel(&self, querier: QuerierWrapper) -> StdResult { + querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetChannel {}) + } + + /// query_state queries the [`state::ContractState`] of this contract + pub fn query_state(&self, querier: QuerierWrapper) -> StdResult { + querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetContractState {}) + } + + /// query_callback_counter queries the [`state::CallbackCounter`] of this contract + pub fn query_callback_counter( + &self, + querier: QuerierWrapper, + ) -> StdResult { + querier.query_wasm_smart(self.addr(), &msg::QueryMsg::GetCallbackCounter {}) + } + + /// update_admin creates a [`WasmMsg::UpdateAdmin`] message targeting this contract + pub fn update_admin(&self, admin: impl Into) -> StdResult { + Ok(WasmMsg::UpdateAdmin { + contract_addr: self.addr().into(), + admin: admin.into(), + } + .into()) + } + + /// clear_admin creates a [`WasmMsg::ClearAdmin`] message targeting this contract + pub fn clear_admin(&self) -> StdResult { + Ok(WasmMsg::ClearAdmin { + contract_addr: self.addr().into(), + } + .into()) + } + + /// migrate creates a [`WasmMsg::Migrate`] message targeting this contract + pub fn migrate( + &self, + msg: impl Into, + new_code_id: u64, + ) -> StdResult { + let msg = to_binary(&msg.into())?; + Ok(WasmMsg::Migrate { + contract_addr: self.addr().into(), + new_code_id, + msg, + } + .into()) + } +} + +impl CwIcaControllerCode { + /// new creates a new [`CwIcaControllerCode`] + pub fn new(code_id: u64) -> Self { + Self(code_id) + } + + /// code_id returns the code id of this code + pub fn code_id(&self) -> u64 { + self.0 + } + + /// instantiate creates a [`WasmMsg::Instantiate`] message targeting this code + pub fn instantiate( + &self, + msg: impl Into, + label: impl Into, + admin: Option>, + ) -> StdResult { + let msg = to_binary(&msg.into())?; + Ok(WasmMsg::Instantiate { + code_id: self.code_id(), + msg, + funds: vec![], + label: label.into(), + admin: admin.map(|s| s.into()), + } + .into()) + } + + /// instantiate2 creates a [`WasmMsg::Instantiate2`] message targeting this code + pub fn instantiate2( + &self, + msg: impl Into, + label: impl Into, + admin: Option>, + salt: impl Into, + ) -> StdResult { + let msg = to_binary(&msg.into())?; + Ok(WasmMsg::Instantiate2 { + code_id: self.code_id(), + msg, + funds: vec![], + label: label.into(), + admin: admin.map(|s| s.into()), + salt: salt.into(), + } + .into()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 371d4450..d23d0252 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,5 +2,6 @@ #![deny(missing_docs)] pub mod contract; +pub mod helpers; pub mod ibc; pub mod types; diff --git a/src/types/error.rs b/src/types/error.rs index 6a34ee0d..2d51eec4 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -24,9 +24,15 @@ pub enum ContractError { #[error("prost encoding error: {0}")] ProstEncodeError(#[from] cosmos_sdk_proto::prost::EncodeError), + #[error("semver parse error: {0}")] + SemverError(#[from] semver::Error), + #[error("unauthorized")] Unauthorized {}, + #[error("invalid migration version: expected {expected}, got {actual}")] + InvalidMigrationVersion { expected: String, actual: String }, + #[error("invalid channel ordering")] InvalidChannelOrdering {}, diff --git a/src/types/msg.rs b/src/types/msg.rs index c99bedd0..8c571155 100644 --- a/src/types/msg.rs +++ b/src/types/msg.rs @@ -76,3 +76,7 @@ pub enum QueryMsg { #[returns(crate::types::state::CallbackCounter)] GetCallbackCounter {}, } + +/// The message to migrate this contract. +#[cw_serde] +pub struct MigrateMsg {}