Skip to content

Commit

Permalink
feat(minor-router): check duplicate chain name when register chain (a…
Browse files Browse the repository at this point in the history
…xelarnetwork#638)

Co-authored-by: Christian Gorenflo <christian@axelar.network>
  • Loading branch information
haiyizxx and cgorenflo authored Oct 2, 2024
1 parent 1775139 commit a74a60a
Show file tree
Hide file tree
Showing 25 changed files with 399 additions and 129 deletions.
10 changes: 8 additions & 2 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 @@ -15,6 +15,7 @@ edition = "2021"
[workspace.dependencies]
alloy-primitives = { version = "0.7.6", default-features = false, features = ["std"] }
alloy-sol-types = { version = "0.7.6", default-features = false, features = ["std"] }
anyhow = "1.0.89"
assert_ok = "1.0"
axelar-wasm-std = { version = "^1.0.0", path = "packages/axelar-wasm-std" }
axelar-wasm-std-derive = { version = "^1.0.0", path = "packages/axelar-wasm-std-derive" }
Expand Down
3 changes: 3 additions & 0 deletions contracts/router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ optimize = """docker run --rm -v "$(pwd)":/code \
"""

[dependencies]
axelar-core-std = { workspace = true }
axelar-wasm-std = { workspace = true, features = ["derive"] }
client = { workspace = true }
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw-storage-plus = { workspace = true }
Expand All @@ -49,6 +51,7 @@ serde_json = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
axelar-core-std = { workspace = true, features = ["test"] }
cw-multi-test = "0.15.1"
hex = { version = "0.4.3", default-features = false }
integration-tests = { workspace = true }
Expand Down
14 changes: 12 additions & 2 deletions contracts/router/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,13 @@ pub fn execute(
msg_id_format,
} => {
let gateway_address = address::validate_cosmwasm_address(deps.api, &gateway_address)?;
execute::register_chain(deps.storage, chain, gateway_address, msg_id_format)
Ok(execute::register_chain(
deps.storage,
deps.querier,
chain,
gateway_address,
msg_id_format,
)?)
}
ExecuteMsg::UpgradeGateway {
chain,
Expand Down Expand Up @@ -129,7 +135,7 @@ pub fn query(
match msg {
QueryMsg::ChainInfo(chain) => to_json_binary(&query::chain_info(deps.storage, chain)?),
QueryMsg::Chains { start_after, limit } => {
to_json_binary(&query::chains(deps, start_after, limit)?)
to_json_binary(&query::chains(deps.storage, start_after, limit)?)
}
QueryMsg::IsEnabled => to_json_binary(&killswitch::is_contract_active(deps.storage)),
}
Expand All @@ -141,6 +147,7 @@ mod test {
use std::collections::HashMap;
use std::str::FromStr;

use axelar_core_std::nexus::test_utils::reply_with_is_chain_registered;
use axelar_wasm_std::err_contains;
use axelar_wasm_std::error::ContractError;
use axelar_wasm_std::msg_id::HexTxHashAndEventIndex;
Expand All @@ -164,6 +171,9 @@ mod test {

fn setup() -> OwnedDeps<MockStorage, MockApi, MockQuerier, Empty> {
let mut deps = mock_dependencies();
deps.querier = deps
.querier
.with_custom_handler(reply_with_is_chain_registered(false));

instantiate(
deps.as_mut(),
Expand Down
55 changes: 47 additions & 8 deletions contracts/router/src/contract/execute.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use std::collections::HashMap;
use std::vec;

use axelar_core_std::nexus;
use axelar_wasm_std::flagset::FlagSet;
use axelar_wasm_std::killswitch;
use axelar_wasm_std::msg_id::{self, MessageIdFormat};
use cosmwasm_std::{to_json_binary, Addr, Event, Response, StdResult, Storage, WasmMsg};
use error_stack::{ensure, report, ResultExt};
use cosmwasm_std::{
to_json_binary, Addr, Event, QuerierWrapper, Response, StdResult, Storage, WasmMsg,
};
use error_stack::{bail, ensure, report, Report, ResultExt};
use itertools::Itertools;
use router_api::error::Error;
use router_api::{ChainEndpoint, ChainName, Gateway, GatewayDirection, Message};
Expand All @@ -18,13 +21,27 @@ use crate::{events, state};

pub fn register_chain(
storage: &mut dyn Storage,
querier: QuerierWrapper,
name: ChainName,
gateway: Addr,
msg_id_format: MessageIdFormat,
) -> Result<Response, Error> {
if find_chain_for_gateway(storage, &gateway)?.is_some() {
return Err(Error::GatewayAlreadyRegistered);
) -> Result<Response, Report<Error>> {
if find_chain_for_gateway(storage, &gateway)
.change_context(Error::StoreFailure)?
.is_some()
{
bail!(Error::GatewayAlreadyRegistered)
}

let client: nexus::Client = client::CosmosClient::new(querier).into();
if client
.is_chain_registered(&name)
.change_context(Error::Nexus)?
{
Err(Report::new(Error::ChainAlreadyExists))
.attach_printable(format!("chain {} already exists in core", name))?
}

chain_endpoints().update(storage, name.clone(), |chain| match chain {
Some(_) => Err(Error::ChainAlreadyExists),
None => Ok(ChainEndpoint {
Expand Down Expand Up @@ -244,15 +261,17 @@ pub fn route_messages(
mod test {
use std::collections::HashMap;

use axelar_core_std::nexus::test_utils::reply_with_is_chain_registered;
use axelar_wasm_std::assert_err_contains;
use axelar_wasm_std::flagset::FlagSet;
use axelar_wasm_std::msg_id::HexTxHashAndEventIndex;
use axelar_wasm_std::msg_id::{HexTxHashAndEventIndex, MessageIdFormat};
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{Addr, Storage};
use cosmwasm_std::{Addr, QuerierWrapper, Storage};
use rand::{random, RngCore};
use router_api::error::Error;
use router_api::{ChainEndpoint, ChainName, CrossChainId, Gateway, GatewayDirection, Message};

use super::{freeze_chains, unfreeze_chains};
use super::{freeze_chains, register_chain, unfreeze_chains};
use crate::contract::execute::route_messages;
use crate::contract::instantiate;
use crate::events::{ChainFrozen, ChainUnfrozen};
Expand Down Expand Up @@ -929,6 +948,26 @@ mod test {
));
}

#[test]
fn register_chain_with_duplicate_chain_name_in_core() {
let mut deps = mock_dependencies();
deps.querier = deps
.querier
.with_custom_handler(reply_with_is_chain_registered(true));

assert_err_contains!(
register_chain(
&mut deps.storage,
QuerierWrapper::new(&deps.querier),
"ethereum".parse().unwrap(),
Addr::unchecked("gateway"),
MessageIdFormat::HexTxHashAndEventIndex
),
Error,
Error::ChainAlreadyExists
);
}

fn assert_chain_endpoint_frozen_status(
storage: &dyn Storage,
chain: ChainName,
Expand Down
5 changes: 5 additions & 0 deletions contracts/router/src/contract/migrations/v0_3_3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const CONFIG: Item<Config> = Item::new("config");
mod test {
use std::collections::HashMap;

use axelar_core_std::nexus::test_utils::reply_with_is_chain_registered;
use axelar_wasm_std::error::ContractError;
use axelar_wasm_std::killswitch;
use axelar_wasm_std::msg_id::MessageIdFormat;
Expand Down Expand Up @@ -108,6 +109,10 @@ mod test {
#[test]
fn migration() {
let mut deps = mock_dependencies();
deps.querier = deps
.querier
.with_custom_handler(reply_with_is_chain_registered(false));

let instantiate_msg = instantiate_0_3_3_contract(deps.as_mut()).unwrap();

let msg = ExecuteMsg::RegisterChain {
Expand Down
34 changes: 23 additions & 11 deletions contracts/router/src/contract/query.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use cosmwasm_std::{Deps, Order, Storage};
use cosmwasm_std::{Order, Storage};
use cw_storage_plus::Bound;
use error_stack::{Result, ResultExt};
use router_api::error::Error;
Expand All @@ -17,15 +17,15 @@ pub fn chain_info(storage: &dyn Storage, chain: ChainName) -> Result<ChainEndpoi
}

pub fn chains(
deps: Deps,
storage: &dyn Storage,
start_after: Option<ChainName>,
limit: Option<u32>,
) -> Result<Vec<ChainEndpoint>, Error> {
let limit = limit.unwrap_or(DEFAULT_LIMIT) as usize;
let start = start_after.map(Bound::exclusive);

chain_endpoints()
.range(deps.storage, start, None, Order::Ascending)
.range(storage, start, None, Order::Ascending)
.take(limit)
.map(|item| {
item.map(|(_, endpoint)| endpoint)
Expand Down Expand Up @@ -105,29 +105,41 @@ mod test {
}

// no pagination
let result = super::chains(deps.as_ref(), None, None).unwrap();
let result = super::chains(deps.as_ref().storage, None, None).unwrap();
assert_eq!(result.len(), 4);
assert_eq!(result, endpoints);

// with limit
let result = super::chains(deps.as_ref(), None, Some(2)).unwrap();
let result = super::chains(deps.as_ref().storage, None, Some(2)).unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result, vec![endpoints[0].clone(), endpoints[1].clone()]);

// with page
let result =
super::chains(deps.as_ref(), Some("c-chain".parse().unwrap()), Some(2)).unwrap();
let result = super::chains(
deps.as_ref().storage,
Some("c-chain".parse().unwrap()),
Some(2),
)
.unwrap();
assert_eq!(result.len(), 1);
assert_eq!(result, vec![endpoints[3].clone()]);

// start after the last chain
let result =
super::chains(deps.as_ref(), Some("d-chain".parse().unwrap()), Some(2)).unwrap();
let result = super::chains(
deps.as_ref().storage,
Some("d-chain".parse().unwrap()),
Some(2),
)
.unwrap();
assert_eq!(result.len(), 0);

// with a key out of the scope
let result =
super::chains(deps.as_ref(), Some("e-chain".parse().unwrap()), Some(2)).unwrap();
let result = super::chains(
deps.as_ref().storage,
Some("e-chain".parse().unwrap()),
Some(2),
)
.unwrap();
assert_eq!(result.len(), 0);
}
}
3 changes: 3 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ optimize = """docker run --rm -v "$(pwd)":/code \
"""

[dependencies]
anyhow = { workspace = true }
axelar-core-std = { workspace = true }
axelar-wasm-std = { workspace = true }
coordinator = { workspace = true }
cosmwasm-std = { workspace = true }
Expand All @@ -43,6 +45,7 @@ report = { workspace = true }
rewards = { workspace = true }
router = { workspace = true }
router-api = { workspace = true }
schemars = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
service-registry = { workspace = true }
Expand Down
14 changes: 10 additions & 4 deletions integration-tests/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
use cosmwasm_std::{Addr, Coin, StdError, StdResult};
use cw_multi_test::{App, AppResponse, Executor};
use cw_multi_test::{AppResponse, Executor};
use error_stack::{report, Result};
use serde::de::DeserializeOwned;
use serde::Serialize;

use crate::protocol::AxelarApp;

pub trait Contract {
type QMsg;
type ExMsg;

fn contract_address(&self) -> Addr;
fn query<T: DeserializeOwned>(&self, app: &App, query_message: &Self::QMsg) -> StdResult<T>
fn query<T: DeserializeOwned>(
&self,
app: &AxelarApp,
query_message: &Self::QMsg,
) -> StdResult<T>
where
Self::QMsg: Serialize,
{
Expand All @@ -19,7 +25,7 @@ pub trait Contract {

fn execute(
&self,
app: &mut App,
app: &mut AxelarApp,
caller: Addr,
execute_message: &Self::ExMsg,
) -> Result<AppResponse, axelar_wasm_std::error::ContractError>
Expand All @@ -32,7 +38,7 @@ pub trait Contract {

fn execute_with_funds(
&self,
app: &mut App,
app: &mut AxelarApp,
caller: Addr,
execute_message: &Self::ExMsg,
funds: &[Coin],
Expand Down
11 changes: 8 additions & 3 deletions integration-tests/src/coordinator_contract.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
use coordinator::contract::{execute, instantiate, query};
use cosmwasm_std::Addr;
use cw_multi_test::{App, ContractWrapper, Executor};
use cw_multi_test::{ContractWrapper, Executor};

use crate::contract::Contract;
use crate::protocol::AxelarApp;

#[derive(Clone)]
pub struct CoordinatorContract {
pub contract_addr: Addr,
}

impl CoordinatorContract {
pub fn instantiate_contract(app: &mut App, governance: Addr, service_registry: Addr) -> Self {
let code = ContractWrapper::new(execute, instantiate, query);
pub fn instantiate_contract(
app: &mut AxelarApp,
governance: Addr,
service_registry: Addr,
) -> Self {
let code = ContractWrapper::new_with_empty(execute, instantiate, query);
let code_id = app.store_code(Box::new(code));

let contract_addr = app
Expand Down
Loading

0 comments on commit a74a60a

Please sign in to comment.