Skip to content

Commit

Permalink
fix(router): migration to delete chains
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcobb23 committed Nov 26, 2024
1 parent 7d715f8 commit 05b12d4
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 101 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
1 change: 1 addition & 0 deletions contracts/router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ mockall = { workspace = true }
msgs-derive = { workspace = true }
report = { workspace = true }
router-api = { workspace = true }
semver = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }

Expand Down
11 changes: 6 additions & 5 deletions contracts/router/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use cosmwasm_std::{
to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, Storage,
};
use router_api::error::Error;
use semver::{Version, VersionReq};

use crate::contract::migrations::v1_0_1;
use crate::contract::migrations::v1_1_1;
use crate::events::RouterInstantiated;
use crate::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use crate::state;
Expand All @@ -18,18 +19,18 @@ mod query;

pub const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME");
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
const BASE_VERSION: &str = "1.0.1";

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(
deps: DepsMut,
_env: Env,
msg: MigrateMsg,
) -> Result<Response, axelar_wasm_std::error::ContractError> {
cw2::assert_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?;
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));

let axelarnet_gateway = address::validate_cosmwasm_address(deps.api, &msg.axelarnet_gateway)?;
v1_0_1::migrate(deps.storage, axelarnet_gateway)?;
v1_1_1::migrate(deps.storage, msg.chains_to_remove)?;

// this needs to be the last thing to do during migration,
// because previous migration steps should check the old version
Expand Down
2 changes: 1 addition & 1 deletion contracts/router/src/contract/migrations/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pub mod v1_0_1;
pub mod v1_1_1;
94 changes: 0 additions & 94 deletions contracts/router/src/contract/migrations/v1_0_1.rs

This file was deleted.

138 changes: 138 additions & 0 deletions contracts/router/src/contract/migrations/v1_1_1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use axelar_wasm_std::flagset::FlagSet;
use axelar_wasm_std::msg_id::MessageIdFormat;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Storage};
use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex};
use error_stack::{Result, ResultExt};
use router_api::error::Error;
use router_api::{Gateway, GatewayDirection};

// the below types and functions are duplicated from the state module, except
// chain names are just stored as String instead of ChainName. This is so we
// can access chains with names that are no longer valid, and were stored
// when the checks on ChainName were less restrictive
#[cw_serde]
struct ChainEndpoint {
pub name: String,
pub gateway: Gateway,
pub frozen_status: FlagSet<GatewayDirection>,
pub msg_id_format: MessageIdFormat,
}

struct ChainEndpointIndexes<'a> {
pub gateway: GatewayIndex<'a>,
}

struct GatewayIndex<'a>(MultiIndex<'a, Addr, ChainEndpoint, String>);

impl<'a> GatewayIndex<'a> {
pub fn new(
idx_fn: fn(&[u8], &ChainEndpoint) -> Addr,
pk_namespace: &'a str,
idx_namespace: &'a str,
) -> Self {
GatewayIndex(MultiIndex::new(idx_fn, pk_namespace, idx_namespace))
}
}

impl<'a> IndexList<ChainEndpoint> for ChainEndpointIndexes<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<ChainEndpoint>> + '_> {
let v: Vec<&dyn Index<ChainEndpoint>> = vec![&self.gateway.0];
Box::new(v.into_iter())
}
}

const CHAINS_PKEY: &str = "chains";
fn chain_endpoints_old<'a>() -> IndexedMap<'a, String, ChainEndpoint, ChainEndpointIndexes<'a>> {
return IndexedMap::new(
CHAINS_PKEY,
ChainEndpointIndexes {
gateway: GatewayIndex::new(
|_pk: &[u8], d: &ChainEndpoint| d.gateway.address.clone(),
CHAINS_PKEY,
"gateways",
),
},
);
}

pub fn migrate(storage: &mut dyn Storage, chains_to_remove: Vec<String>) -> Result<(), Error> {
for chain in chains_to_remove {
chain_endpoints_old()
.remove(storage, chain)
.change_context(Error::StoreFailure)?;
}
Ok(())
}
#[cfg(test)]
mod test {
#![allow(deprecated)]

use assert_ok::assert_ok;
use axelar_wasm_std::msg_id::MessageIdFormat;
use cosmwasm_std::testing::mock_dependencies;
use cosmwasm_std::Addr;
use itertools::Itertools;
use router_api::{ChainName, Gateway, GatewayDirection};

use super::{chain_endpoints_old, migrate, ChainEndpoint};
use crate::state::{self, chain_endpoints};

#[test]
fn can_delete_chains() {
let mut deps = mock_dependencies();
let good_chain_names: Vec<ChainName> = ["ethereum", "avalanche"]
.into_iter()
.map(|name| ChainName::try_from(name).unwrap())
.collect();
for chain_name in &good_chain_names {
state::chain_endpoints()
.save(
deps.as_mut().storage,
chain_name.clone(),
&router_api::ChainEndpoint {
name: chain_name.clone(),
gateway: Gateway {
address: Addr::unchecked("gateway_address"),
},
frozen_status: GatewayDirection::None.into(),
msg_id_format: MessageIdFormat::HexTxHashAndEventIndex,
},
)
.unwrap();
}

let bad_chain_name = "some really really long chain name that is not valid";
chain_endpoints_old()
.save(
deps.as_mut().storage,
bad_chain_name.to_string(),
&ChainEndpoint {
name: bad_chain_name.to_string(),
gateway: Gateway {
address: Addr::unchecked("gateway_address"),
},
frozen_status: GatewayDirection::None.into(),
msg_id_format: MessageIdFormat::HexTxHashAndEventIndex,
},
)
.unwrap();

assert_ok!(migrate(
deps.as_mut().storage,
vec![good_chain_names[0].to_string(), bad_chain_name.to_string()]
));

let chains: Vec<ChainName> = assert_ok!(chain_endpoints()
.range(
deps.as_mut().storage,
None,
None,
cosmwasm_std::Order::Ascending
)
.map(|item| { item.map(|(_, endpoint)| endpoint.name) })
.try_collect());

assert_eq!(chains, vec![good_chain_names[1].clone()]);
}
}
2 changes: 1 addition & 1 deletion contracts/router/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct InstantiateMsg {

#[cw_serde]
pub struct MigrateMsg {
pub axelarnet_gateway: String,
pub chains_to_remove: Vec<String>,
}

// these messages are extracted into a separate package to avoid circular dependencies
Expand Down
1 change: 1 addition & 0 deletions packages/axelar-wasm-std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
8 changes: 8 additions & 0 deletions packages/axelar-wasm-std/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ impl From<cw2::VersionError> for ContractError {
}
}

impl From<semver::Error> for ContractError {
fn from(err: semver::Error) -> Self {
ContractError {
report: report!(err).change_context(Error::Report),
}
}
}

impl From<permission_control::Error> for ContractError {
fn from(err: permission_control::Error) -> Self {
ContractError {
Expand Down

0 comments on commit 05b12d4

Please sign in to comment.