From 801b61fc3200d8b2f8b5f5f91812c74f3c9ac54d Mon Sep 17 00:00:00 2001 From: eguajardo Date: Wed, 10 Apr 2024 09:02:24 -0600 Subject: [PATCH] refactor(connection-router): remove mock contracts from tests (#340) * moved couple of tests to unit tests * moved more tests to contract unit tests * move test to integration-tests module * move test to integration tests module and remove unused files * added comment --- contracts/connection-router/src/contract.rs | 1259 ++++++++++++++++ contracts/connection-router/tests/mock.rs | 88 -- contracts/connection-router/tests/test.rs | 1290 ----------------- .../connection-router/tests/test_utils/mod.rs | 53 - .../tests/chain_freeze_unfreeze.rs | 93 ++ integration-tests/tests/message_routing.rs | 52 +- integration-tests/tests/test_utils/mod.rs | 63 +- 7 files changed, 1465 insertions(+), 1433 deletions(-) delete mode 100644 contracts/connection-router/tests/mock.rs delete mode 100644 contracts/connection-router/tests/test.rs delete mode 100644 contracts/connection-router/tests/test_utils/mod.rs create mode 100644 integration-tests/tests/chain_freeze_unfreeze.rs diff --git a/contracts/connection-router/src/contract.rs b/contracts/connection-router/src/contract.rs index 0d1b44504..b2b282a6a 100644 --- a/contracts/connection-router/src/contract.rs +++ b/contracts/connection-router/src/contract.rs @@ -112,3 +112,1262 @@ pub fn query( } .map_err(axelar_wasm_std::ContractError::from) } + +#[cfg(test)] +mod test { + use std::{collections::HashMap, str::FromStr}; + + use crate::state::CONFIG; + + use super::*; + + use connection_router_api::{ + error::Error, ChainName, CrossChainId, GatewayDirection, Message, CHAIN_NAME_DELIMITER, + }; + use cosmwasm_std::{ + testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier, MockStorage}, + Addr, CosmosMsg, Empty, OwnedDeps, WasmMsg, + }; + + const ADMIN_ADDRESS: &str = "admin"; + const GOVERNANCE_ADDRESS: &str = "governance"; + const NEXUS_GATEWAY_ADDRESS: &str = "nexus_gateway"; + const UNAUTHORIZED_ADDRESS: &str = "unauthorized"; + + fn setup() -> OwnedDeps { + let mut deps = mock_dependencies(); + + let config = Config { + admin: Addr::unchecked(ADMIN_ADDRESS), + governance: Addr::unchecked(GOVERNANCE_ADDRESS), + nexus_gateway: Addr::unchecked(NEXUS_GATEWAY_ADDRESS), + }; + CONFIG.save(deps.as_mut().storage, &config).unwrap(); + + deps + } + + struct Chain { + chain_name: ChainName, + gateway: Addr, + } + + fn make_chain(name: &str) -> Chain { + Chain { + chain_name: name.parse().unwrap(), + gateway: Addr::unchecked(name), + } + } + + fn register_chain(deps: DepsMut, chain: &Chain) { + execute( + deps, + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::RegisterChain { + chain: chain.chain_name.clone(), + gateway_address: chain.gateway.to_string().try_into().unwrap(), + }, + ) + .unwrap(); + } + + #[allow(clippy::arithmetic_side_effects)] + fn generate_messages( + src_chain: &Chain, + dest_chain: &Chain, + nonce: &mut usize, + count: usize, + ) -> Vec { + let mut msgs = vec![]; + for x in 0..count { + *nonce += 1; + let id = format!("tx_id:{}", nonce); + msgs.push(Message { + cc_id: CrossChainId { + id: id.parse().unwrap(), + chain: src_chain.chain_name.clone(), + }, + destination_address: "idc".parse().unwrap(), + destination_chain: dest_chain.chain_name.clone(), + source_address: "idc".parse().unwrap(), + payload_hash: [x as u8; 32], + }) + } + msgs + } + + pub fn assert_contract_err_strings_equal( + actual: impl Into, + expected: impl Into, + ) { + assert_eq!(actual.into().to_string(), expected.into().to_string()); + } + + pub fn assert_messages_in_cosmos_msg( + contract_addr: String, + messages: Vec, + cosmos_msg: CosmosMsg, + ) { + assert_eq!( + CosmosMsg::Wasm(WasmMsg::Execute { + contract_addr, + msg: to_json_binary(&gateway_api::msg::ExecuteMsg::RouteMessages(messages,)) + .unwrap(), + funds: vec![], + }), + cosmos_msg + ); + } + + #[test] + fn successful_routing() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let nonce: &mut usize = &mut 0; + let messages = generate_messages(ð, &polygon, nonce, 255); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_messages_in_cosmos_msg( + polygon.gateway.to_string(), + messages.clone(), + res.messages[0].msg.clone(), + ); + + // try to route twice + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ); + + assert!(res.is_ok(), "{:?}", res); + } + + #[test] + fn wrong_source_chain() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let messages = generate_messages(ð, &polygon, &mut 0, 1); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages), + ) + .unwrap_err(); + + assert_contract_err_strings_equal(err, Error::WrongSourceChain); + } + + #[test] + fn multi_chain_route() { + let mut deps = setup(); + let chains = vec![ + make_chain("ethereum"), + make_chain("polygon"), + make_chain("osmosis"), + make_chain("avalanche"), + make_chain("moonbeam"), + ]; + for c in &chains { + register_chain(deps.as_mut(), c); + } + + let nonce = &mut 0; + let mut all_msgs_by_dest = HashMap::new(); + let mut all_msgs_by_src = HashMap::new(); + for d in &chains { + let mut msgs = vec![]; + for s in &chains { + let sending = generate_messages(s, d, nonce, 50); + + all_msgs_by_src + .entry(s.chain_name.to_string()) + .or_insert(vec![]) + .append(&mut sending.clone()); + + msgs.append(&mut sending.clone()); + } + all_msgs_by_dest.insert(d.chain_name.to_string(), msgs); + } + + for s in &chains { + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(s.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages( + all_msgs_by_src + .get(&s.chain_name.to_string()) + .unwrap() + .clone(), + ), + ) + .unwrap(); + + assert_eq!(res.messages.len(), chains.len()); + + for (i, d) in chains.iter().enumerate() { + assert_messages_in_cosmos_msg( + d.chain_name.to_string(), + all_msgs_by_dest + .get(&d.chain_name.to_string()) + .unwrap() + .clone() + .into_iter() + .filter(|m| m.cc_id.chain == s.chain_name) + .collect::>(), + res.messages[i].msg.clone(), + ); + } + } + } + + #[test] + fn authorization() { + let mut deps = setup(); + let chain = make_chain("ethereum"); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(UNAUTHORIZED_ADDRESS, &[]), + ExecuteMsg::RegisterChain { + chain: chain.chain_name.clone(), + gateway_address: chain.gateway.to_string().try_into().unwrap(), + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::Unauthorized); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::RegisterChain { + chain: chain.chain_name.clone(), + gateway_address: chain.gateway.to_string().try_into().unwrap(), + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::Unauthorized); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::RegisterChain { + chain: chain.chain_name.clone(), + gateway_address: chain.gateway.to_string().try_into().unwrap(), + }, + ); + assert!(res.is_ok()); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(UNAUTHORIZED_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: chain.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::Unauthorized); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: chain.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::Unauthorized); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: chain.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ); + assert!(res.is_ok()); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(UNAUTHORIZED_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: chain.chain_name.clone(), + direction: GatewayDirection::None, + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::Unauthorized); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: chain.chain_name.clone(), + direction: GatewayDirection::None, + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::Unauthorized); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: chain.chain_name.clone(), + direction: GatewayDirection::None, + }, + ); + assert!(res.is_ok()); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(UNAUTHORIZED_ADDRESS, &[]), + ExecuteMsg::UpgradeGateway { + chain: chain.chain_name.clone(), + contract_address: Addr::unchecked("new gateway") + .to_string() + .try_into() + .unwrap(), + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::Unauthorized); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UpgradeGateway { + chain: chain.chain_name.clone(), + contract_address: Addr::unchecked("new gateway") + .to_string() + .try_into() + .unwrap(), + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::Unauthorized); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::UpgradeGateway { + chain: chain.chain_name.clone(), + contract_address: Addr::unchecked("new gateway") + .to_string() + .try_into() + .unwrap(), + }, + ); + assert!(res.is_ok()); + } + + #[test] + fn upgrade_gateway_outgoing() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + let new_gateway = Addr::unchecked("new_gateway"); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::UpgradeGateway { + chain: polygon.chain_name.clone(), + contract_address: new_gateway.to_string().try_into().unwrap(), + }, + ) + .unwrap(); + + let messages = &generate_messages(ð, &polygon, &mut 0, 1); + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_messages_in_cosmos_msg( + new_gateway.to_string(), + messages.clone(), + res.messages[0].msg.clone(), + ); + } + + #[test] + fn upgrade_gateway_incoming() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + let new_gateway = Addr::unchecked("new_gateway"); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::UpgradeGateway { + chain: polygon.chain_name.clone(), + contract_address: new_gateway.to_string().try_into().unwrap(), + }, + ) + .unwrap(); + + let messages = &generate_messages(&polygon, ð, &mut 0, 1); + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::GatewayNotRegistered); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(new_gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_messages_in_cosmos_msg( + eth.gateway.to_string(), + messages.clone(), + res.messages[0].msg.clone(), + ); + } + + #[test] + fn register_chain_test() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + + let message = &generate_messages(ð, &polygon, &mut 0, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::GatewayNotRegistered); + + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + } + + #[test] + fn chain_already_registered() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + register_chain(deps.as_mut(), ð); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::RegisterChain { + chain: eth.chain_name, + gateway_address: Addr::unchecked("new gateway") + .to_string() + .try_into() + .unwrap(), + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::ChainAlreadyExists); + + // case insensitive + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::RegisterChain { + chain: ChainName::from_str("ETHEREUM").unwrap(), + gateway_address: Addr::unchecked("new gateway") + .to_string() + .try_into() + .unwrap(), + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::ChainAlreadyExists); + } + + #[test] + fn invalid_chain_name() { + assert_contract_err_strings_equal( + ChainName::from_str(format!("bad{}", CHAIN_NAME_DELIMITER).as_str()).unwrap_err(), + Error::InvalidChainName, + ); + + assert_contract_err_strings_equal( + ChainName::from_str("").unwrap_err(), + Error::InvalidChainName, + ); + } + + #[test] + fn gateway_already_registered() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::RegisterChain { + chain: polygon.chain_name.clone(), + gateway_address: eth.gateway.to_string().try_into().unwrap(), + }, + ) + .unwrap_err(); + assert_contract_err_strings_equal(err, Error::GatewayAlreadyRegistered); + + register_chain(deps.as_mut(), &polygon); + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(GOVERNANCE_ADDRESS, &[]), + ExecuteMsg::UpgradeGateway { + chain: eth.chain_name, + contract_address: polygon.gateway.to_string().try_into().unwrap(), + }, + ) + .unwrap_err(); + + assert_contract_err_strings_equal(err, Error::GatewayAlreadyRegistered); + } + + #[test] + fn freeze_incoming() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Incoming, + }, + ) + .unwrap(); + + // can't route from frozen incoming gateway + let message = &generate_messages(&polygon, ð, &mut 0, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + + let messages = &generate_messages(ð, &polygon, &mut 0, 1); + // can still route to chain + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_messages_in_cosmos_msg( + polygon.gateway.to_string(), + messages.clone(), + res.messages[0].msg.clone(), + ); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Incoming, + }, + ); + assert!(res.is_ok()); + + let message = &generate_messages(&polygon, ð, &mut 0, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + } + + #[test] + fn freeze_outgoing() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + // freeze outgoing + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Outgoing, + }, + ); + assert!(res.is_ok()); + + // can still send to the chain, messages will queue up + let messages = &generate_messages(ð, &polygon, &mut 0, 1); + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Outgoing, + }, + ); + assert!(res.is_ok()); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(messages.clone()), + ) + .unwrap(); + + assert_eq!(res.messages.len(), 1); + assert_messages_in_cosmos_msg( + polygon.gateway.to_string(), + messages.clone(), + res.messages[0].msg.clone(), + ); + } + + #[test] + fn freeze_chain() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let nonce = &mut 0; + + // route a message first + let routed_msg = &generate_messages(ð, &polygon, nonce, 1)[0]; + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![routed_msg.clone()]), + ) + .unwrap(); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ); + assert!(res.is_ok()); + + let msg = &generate_messages(ð, &polygon, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![msg.clone()]), + ) + .unwrap_err(); + // can't route to frozen chain + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + + // can't route from frozen chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + + // unfreeze and test that everything works correctly + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ) + .unwrap(); + + // can route to the chain now + let message = &generate_messages(ð, &polygon, nonce, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + + // can route from the chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + } + + #[test] + fn unfreeze_incoming() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ); + assert!(res.is_ok()); + + let nonce = &mut 0; + + // unfreeze incoming + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Incoming, + }, + ) + .unwrap(); + + // can route from the chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + + let message = &generate_messages(ð, &polygon, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + // can't route to the chain + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + } + + #[test] + fn unfreeze_outgoing() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ); + assert!(res.is_ok()); + + let nonce = &mut 0; + + // unfreeze outgoing + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Outgoing, + }, + ) + .unwrap(); + + // can't route from frozen chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + + // can route to the chain now + let message = &generate_messages(ð, &polygon, nonce, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + } + + #[test] + fn freeze_incoming_then_outgoing() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Incoming, + }, + ) + .unwrap(); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Outgoing, + }, + ) + .unwrap(); + + let nonce = &mut 0; + // can't route to frozen chain + let message = &generate_messages(ð, &polygon, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + + // can't route from frozen chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + } + + #[test] + fn freeze_outgoing_then_incoming() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Outgoing, + }, + ) + .unwrap(); + + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Incoming, + }, + ) + .unwrap(); + + let nonce = &mut 0; + // can't route to frozen chain + let message = &generate_messages(ð, &polygon, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + + // can't route from frozen chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + } + + #[test] + fn unfreeze_incoming_then_outgoing() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ); + assert!(res.is_ok()); + + // unfreeze incoming + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Incoming, + }, + ) + .unwrap(); + + // unfreeze outgoing + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Outgoing, + }, + ) + .unwrap(); + + // can route to the chain now + let nonce = &mut 0; + let message = &generate_messages(ð, &polygon, nonce, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + + // can route from the chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + } + + #[test] + fn unfreeze_outgoing_then_incoming() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ); + assert!(res.is_ok()); + + // unfreeze outgoing + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Outgoing, + }, + ) + .unwrap(); + + // unfreeze incoming + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Incoming, + }, + ) + .unwrap(); + + // can route to the chain now + let nonce = &mut 0; + let message = &generate_messages(ð, &polygon, nonce, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + + // can route from the chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ); + assert!(res.is_ok()); + } + + #[test] + fn unfreeze_nothing() { + let mut deps = setup(); + let eth = make_chain("ethereum"); + let polygon = make_chain("polygon"); + register_chain(deps.as_mut(), ð); + register_chain(deps.as_mut(), &polygon); + + let res = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::FreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::Bidirectional, + }, + ); + assert!(res.is_ok()); + + // unfreeze nothing + let _ = execute( + deps.as_mut(), + mock_env(), + mock_info(ADMIN_ADDRESS, &[]), + ExecuteMsg::UnfreezeChain { + chain: polygon.chain_name.clone(), + direction: GatewayDirection::None, + }, + ) + .unwrap(); + + let nonce = &mut 0; + let message = &generate_messages(ð, &polygon, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(eth.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + + // can't route from frozen chain + let message = &generate_messages(&polygon, ð, nonce, 1)[0]; + let err = execute( + deps.as_mut(), + mock_env(), + mock_info(polygon.gateway.as_str(), &[]), + ExecuteMsg::RouteMessages(vec![message.clone()]), + ) + .unwrap_err(); + assert_contract_err_strings_equal( + err, + Error::ChainFrozen { + chain: polygon.chain_name.clone(), + }, + ); + } +} diff --git a/contracts/connection-router/tests/mock.rs b/contracts/connection-router/tests/mock.rs deleted file mode 100644 index 18fbcb472..000000000 --- a/contracts/connection-router/tests/mock.rs +++ /dev/null @@ -1,88 +0,0 @@ -use connection_router_api::error::Error; -use connection_router_api::{CrossChainId, Message}; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ - to_json_binary, Addr, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, -}; -use cw_multi_test::{App, ContractWrapper, Executor}; -use cw_storage_plus::Map; - -const MOCK_GATEWAY_MESSAGES: Map = Map::new("gateway_messages"); - -#[cw_serde] -pub enum MockGatewayExecuteMsg { - RouteMessages(Vec), -} - -pub fn mock_gateway_execute( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: MockGatewayExecuteMsg, -) -> Result { - match msg { - MockGatewayExecuteMsg::RouteMessages(messages) => { - for m in messages { - MOCK_GATEWAY_MESSAGES.save(deps.storage, m.cc_id.clone(), &m)?; - } - Ok(Response::new()) - } - } -} - -#[cw_serde] -pub enum MockGatewayQueryMsg { - GetOutgoingMessages { ids: Vec }, -} -pub fn mock_gateway_query(deps: Deps, _env: Env, msg: MockGatewayQueryMsg) -> StdResult { - let mut msgs = vec![]; - - match msg { - MockGatewayQueryMsg::GetOutgoingMessages { ids } => { - for id in ids { - if let Some(m) = MOCK_GATEWAY_MESSAGES.may_load(deps.storage, id)? { - msgs.push(m) - } - } - } - } - to_json_binary(&msgs) -} - -pub fn get_gateway_messages( - app: &mut App, - gateway_address: Addr, - msgs: &[Message], -) -> Vec { - app.wrap() - .query_wasm_smart( - gateway_address, - &MockGatewayQueryMsg::GetOutgoingMessages { - ids: msgs.iter().map(|msg| msg.cc_id.clone()).collect(), - }, - ) - .unwrap() -} - -pub fn make_mock_gateway(app: &mut App) -> Addr { - let code = ContractWrapper::new( - mock_gateway_execute, - |_, _, _, _: connection_router::msg::InstantiateMsg| Ok::(Response::new()), - mock_gateway_query, - ); - let code_id = app.store_code(Box::new(code)); - - app.instantiate_contract( - code_id, - Addr::unchecked("sender"), - &connection_router::msg::InstantiateMsg { - admin_address: Addr::unchecked("admin").to_string(), - governance_address: Addr::unchecked("governance").to_string(), - nexus_gateway: Addr::unchecked("nexus_gateway").to_string(), - }, - &[], - "Contract", - None, - ) - .unwrap() -} diff --git a/contracts/connection-router/tests/test.rs b/contracts/connection-router/tests/test.rs deleted file mode 100644 index abb24c110..000000000 --- a/contracts/connection-router/tests/test.rs +++ /dev/null @@ -1,1290 +0,0 @@ -pub mod mock; -mod test_utils; - -use std::str::FromStr; -use std::{collections::HashMap, vec}; - -use connection_router_api::error::Error; -use connection_router_api::msg::ExecuteMsg; -use connection_router_api::{ - ChainName, CrossChainId, GatewayDirection, Message, CHAIN_NAME_DELIMITER, -}; -use cosmwasm_std::Addr; -use cw_multi_test::App; -use integration_tests::contract::Contract; - -use crate::test_utils::ConnectionRouterContract; - -struct TestConfig { - app: App, - admin_address: Addr, - governance_address: Addr, - connection_router: ConnectionRouterContract, -} - -struct Chain { - chain_name: ChainName, - gateway: Addr, -} - -fn setup() -> TestConfig { - let mut app = App::default(); - - let admin_address = Addr::unchecked("admin"); - let governance_address = Addr::unchecked("governance"); - let nexus_gateway = Addr::unchecked("nexus_gateway"); - - let connection_router = ConnectionRouterContract::instantiate_contract( - &mut app, - admin_address.clone(), - governance_address.clone(), - nexus_gateway.clone(), - ); - - TestConfig { - app, - admin_address, - governance_address, - connection_router, - } -} - -fn make_chain(name: &str, config: &mut TestConfig) -> Chain { - let gateway = mock::make_mock_gateway(&mut config.app); - Chain { - chain_name: name.parse().unwrap(), - gateway, - } -} - -fn register_chain(config: &mut TestConfig, chain: &Chain) { - let _ = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::RegisterChain { - chain: chain.chain_name.clone(), - gateway_address: chain.gateway.to_string().try_into().unwrap(), - }, - ) - .unwrap(); -} - -#[allow(clippy::arithmetic_side_effects)] -fn generate_messages( - src_chain: &Chain, - dest_chain: &Chain, - nonce: &mut usize, - count: usize, -) -> Vec { - let mut msgs = vec![]; - for x in 0..count { - *nonce += 1; - let id = format!("tx_id:{}", nonce); - msgs.push(Message { - cc_id: CrossChainId { - id: id.parse().unwrap(), - chain: src_chain.chain_name.clone(), - }, - destination_address: "idc".parse().unwrap(), - destination_chain: dest_chain.chain_name.clone(), - source_address: "idc".parse().unwrap(), - payload_hash: [x as u8; 32], - }) - } - msgs -} - -// tests that each message is properly delivered -#[test] -fn route() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let nonce: &mut usize = &mut 0; - let messages = generate_messages(ð, &polygon, nonce, 255); - - let _ = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(messages.clone()), - ) - .unwrap(); - - let outgoing_messages = mock::get_gateway_messages(&mut config.app, polygon.gateway, &messages); - - assert_eq!(messages.len(), outgoing_messages.len()); - assert_eq!(messages, outgoing_messages); - - // try to route twice - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(messages.clone()), - ); - - assert!(res.is_ok()); -} - -#[test] -fn wrong_source_chain() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let messages = &generate_messages(ð, &polygon, &mut 0, 1)[0]; - - let err = config - .connection_router - .execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![messages.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::WrongSourceChain); -} - -#[test] -fn multi_chain_route() { - let mut config = setup(); - let chains = vec![ - make_chain("ethereum", &mut config), - make_chain("polygon", &mut config), - make_chain("osmosis", &mut config), - make_chain("avalanche", &mut config), - make_chain("moonbeam", &mut config), - ]; - for c in &chains { - register_chain(&mut config, c); - } - - let nonce = &mut 0; - let mut all_msgs_by_dest = HashMap::new(); - let mut all_msgs_by_src = HashMap::new(); - for d in &chains { - let mut msgs = vec![]; - for s in &chains { - let mut sending = generate_messages(s, d, nonce, 50); - - all_msgs_by_src - .entry(s.chain_name.to_string()) - .or_insert(vec![]) - .append(&mut sending); - - msgs.append(&mut sending); - } - all_msgs_by_dest.insert(d.chain_name.to_string(), msgs); - } - - for chain in &chains { - let res = config.connection_router.execute( - &mut config.app, - chain.gateway.clone(), - &ExecuteMsg::RouteMessages( - all_msgs_by_src - .get_mut(&chain.chain_name.to_string()) - .unwrap() - .clone(), - ), - ); - assert!(res.is_ok()); - } - - for chain in &chains { - let expected = all_msgs_by_dest.get(&chain.chain_name.to_string()).unwrap(); - let actual = mock::get_gateway_messages(&mut config.app, chain.gateway.clone(), expected); - - assert_eq!(expected.len(), actual.len()); - assert_eq!(expected, &actual); - } -} - -#[test] -fn authorization() { - let mut config = setup(); - let chain = make_chain("ethereum", &mut config); - - let err = config - .connection_router - .execute( - &mut config.app, - Addr::unchecked("random"), - &ExecuteMsg::RegisterChain { - chain: chain.chain_name.clone(), - gateway_address: chain.gateway.to_string().try_into().unwrap(), - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::Unauthorized); - - let err = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::RegisterChain { - chain: chain.chain_name.clone(), - gateway_address: chain.gateway.to_string().try_into().unwrap(), - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::Unauthorized); - - let res = config.connection_router.execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::RegisterChain { - chain: chain.chain_name.clone(), - gateway_address: chain.gateway.to_string().try_into().unwrap(), - }, - ); - assert!(res.is_ok()); - - let err = config - .connection_router - .execute( - &mut config.app, - Addr::unchecked("random"), - &ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::Unauthorized); - - let err = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::Unauthorized); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ); - assert!(res.is_ok()); - - let err = config - .connection_router - .execute( - &mut config.app, - Addr::unchecked("random"), - &ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::None, - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::Unauthorized); - - let err = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::None, - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::Unauthorized); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: chain.chain_name.clone(), - direction: GatewayDirection::None, - }, - ); - assert!(res.is_ok()); - - let err = config - .connection_router - .execute( - &mut config.app, - Addr::unchecked("random"), - &ExecuteMsg::UpgradeGateway { - chain: chain.chain_name.clone(), - contract_address: Addr::unchecked("new gateway") - .to_string() - .try_into() - .unwrap(), - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::Unauthorized); - - let err = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UpgradeGateway { - chain: chain.chain_name.clone(), - contract_address: Addr::unchecked("new gateway") - .to_string() - .try_into() - .unwrap(), - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::Unauthorized); - - let res = config.connection_router.execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::UpgradeGateway { - chain: chain.chain_name.clone(), - contract_address: Addr::unchecked("new gateway") - .to_string() - .try_into() - .unwrap(), - }, - ); - assert!(res.is_ok()); -} - -#[test] -fn upgrade_gateway_outgoing() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - let new_gateway = mock::make_mock_gateway(&mut config.app); - - let _ = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::UpgradeGateway { - chain: polygon.chain_name.clone(), - contract_address: new_gateway.to_string().try_into().unwrap(), - }, - ) - .unwrap(); - - let message = &generate_messages(ð, &polygon, &mut 0, 1)[0]; - let _ = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap(); - - let outgoing_messages = - mock::get_gateway_messages(&mut config.app, new_gateway, &[message.clone()]); - assert_eq!(outgoing_messages.len(), 1); - assert_eq!(message.clone(), outgoing_messages[0]); - - let outgoing_messages = - mock::get_gateway_messages(&mut config.app, polygon.gateway, &[message.clone()]); - assert_eq!(outgoing_messages.len(), 0); -} - -#[test] -fn upgrade_gateway_incoming() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - let new_gateway = mock::make_mock_gateway(&mut config.app); - - let _ = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::UpgradeGateway { - chain: polygon.chain_name.clone(), - contract_address: new_gateway.to_string().try_into().unwrap(), - }, - ) - .unwrap(); - - let message = &generate_messages(&polygon, ð, &mut 0, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::GatewayNotRegistered); - - let res = config.connection_router.execute( - &mut config.app, - new_gateway, - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); - - let messages = mock::get_gateway_messages(&mut config.app, eth.gateway, &[message.clone()]); - assert_eq!(messages.len(), 1); - assert_eq!(messages[0], message.clone()); -} - -#[test] -fn register_chain_test() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - - let message = &generate_messages(ð, &polygon, &mut 0, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::GatewayNotRegistered); - - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); -} - -#[test] -fn chain_already_registered() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - register_chain(&mut config, ð); - - let err = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::RegisterChain { - chain: eth.chain_name, - gateway_address: Addr::unchecked("new gateway") - .to_string() - .try_into() - .unwrap(), - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::ChainAlreadyExists); - - // case insensitive - let err = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::RegisterChain { - chain: ChainName::from_str("ETHEREUM").unwrap(), - gateway_address: Addr::unchecked("new gateway") - .to_string() - .try_into() - .unwrap(), - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::ChainAlreadyExists); -} - -#[test] -fn invalid_chain_name() { - test_utils::are_contract_err_strings_equal( - ChainName::from_str(format!("bad{}", CHAIN_NAME_DELIMITER).as_str()).unwrap_err(), - Error::InvalidChainName, - ); - - test_utils::are_contract_err_strings_equal( - ChainName::from_str("").unwrap_err(), - Error::InvalidChainName, - ); -} - -#[test] -fn gateway_already_registered() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - - let err = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::RegisterChain { - chain: polygon.chain_name.clone(), - gateway_address: eth.gateway.to_string().try_into().unwrap(), - }, - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal(err, Error::GatewayAlreadyRegistered); - - register_chain(&mut config, &polygon); - let err = config - .connection_router - .execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::UpgradeGateway { - chain: eth.chain_name, - contract_address: polygon.gateway.to_string().try_into().unwrap(), - }, - ) - .unwrap_err(); - - test_utils::are_contract_err_strings_equal(err, Error::GatewayAlreadyRegistered); -} - -#[test] -fn freeze_incoming() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, - }, - ) - .unwrap(); - - // can't route from frozen incoming gateway - let message = &generate_messages(&polygon, ð, &mut 0, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); - - let message = &generate_messages(ð, &polygon, &mut 0, 1)[0]; - // can still route to chain - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); - - let messages = - mock::get_gateway_messages(&mut config.app, polygon.gateway.clone(), &[message.clone()]); - assert_eq!(&messages[0], message); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, - }, - ); - assert!(res.is_ok()); - - let message = &generate_messages(&polygon, ð, &mut 0, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); -} - -#[test] -fn freeze_outgoing() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - // freeze outgoing - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, - }, - ); - assert!(res.is_ok()); - - // can still send to the chain, messages will queue up - let message = &generate_messages(ð, &polygon, &mut 0, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, - }, - ); - assert!(res.is_ok()); - - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); - let messages = mock::get_gateway_messages(&mut config.app, polygon.gateway, &[message.clone()]); - assert_eq!(messages.len(), 1); - assert_eq!(messages[0], message.clone()); -} - -#[test] -fn freeze_chain() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let nonce = &mut 0; - - // route a message first - let routed_msg = &generate_messages(ð, &polygon, nonce, 1)[0]; - let _ = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![routed_msg.clone()]), - ) - .unwrap(); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ); - assert!(res.is_ok()); - - let msg = &generate_messages(ð, &polygon, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![msg.clone()]), - ) - .unwrap_err(); - // can't route to frozen chain - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); - - // can't route from frozen chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); - - // unfreeze and test that everything works correctly - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ) - .unwrap(); - - // routed message should have been preserved - let outgoing_messages = mock::get_gateway_messages( - &mut config.app, - polygon.gateway.clone(), - &[routed_msg.clone()], - ); - assert_eq!(1, outgoing_messages.len()); - assert_eq!(routed_msg.clone(), outgoing_messages[0]); - - // can route to the chain now - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); - - // can route from the chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); -} - -#[test] -fn unfreeze_incoming() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ); - assert!(res.is_ok()); - - let nonce = &mut 0; - - // unfreeze incoming - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, - }, - ) - .unwrap(); - - // can route from the chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); - - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - // can't route to the chain - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); -} - -#[test] -fn unfreeze_outgoing() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ); - assert!(res.is_ok()); - - let nonce = &mut 0; - - // unfreeze outgoing - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, - }, - ) - .unwrap(); - - // can't route from frozen chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); - - // can route to the chain now - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); -} - -#[test] -fn freeze_incoming_then_outgoing() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, - }, - ) - .unwrap(); - - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, - }, - ) - .unwrap(); - - let nonce = &mut 0; - // can't route to frozen chain - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); - - // can't route from frozen chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); -} - -#[test] -fn freeze_outgoing_then_incoming() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, - }, - ) - .unwrap(); - - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, - }, - ) - .unwrap(); - - let nonce = &mut 0; - // can't route to frozen chain - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); - - // can't route from frozen chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); -} - -#[test] -fn unfreeze_incoming_then_outgoing() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ); - assert!(res.is_ok()); - - // unfreeze incoming - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, - }, - ) - .unwrap(); - - // unfreeze outgoing - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, - }, - ) - .unwrap(); - - // can route to the chain now - let nonce = &mut 0; - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); - - // can route from the chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); -} - -#[test] -fn unfreeze_outgoing_then_incoming() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ); - assert!(res.is_ok()); - - // unfreeze outgoing - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Outgoing, - }, - ) - .unwrap(); - - // unfreeze incoming - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Incoming, - }, - ) - .unwrap(); - - // can route to the chain now - let nonce = &mut 0; - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); - - // can route from the chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let res = config.connection_router.execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_ok()); -} - -#[test] -fn unfreeze_nothing() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let res = config.connection_router.execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::FreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::Bidirectional, - }, - ); - assert!(res.is_ok()); - - // unfreeze nothing - let _ = config - .connection_router - .execute( - &mut config.app, - config.admin_address.clone(), - &ExecuteMsg::UnfreezeChain { - chain: polygon.chain_name.clone(), - direction: GatewayDirection::None, - }, - ) - .unwrap(); - - let nonce = &mut 0; - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); - - // can't route from frozen chain - let message = &generate_messages(&polygon, ð, nonce, 1)[0]; - let err = config - .connection_router - .execute( - &mut config.app, - polygon.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ) - .unwrap_err(); - test_utils::are_contract_err_strings_equal( - err, - Error::ChainFrozen { - chain: polygon.chain_name.clone(), - }, - ); -} - -#[test] -fn bad_gateway() { - let mut config = setup(); - let eth = make_chain("ethereum", &mut config); - let polygon = make_chain("polygon", &mut config); - - register_chain(&mut config, ð); - register_chain(&mut config, &polygon); - - let res = config.connection_router.execute( - &mut config.app, - config.governance_address.clone(), - &ExecuteMsg::UpgradeGateway { - chain: polygon.chain_name.clone(), - contract_address: Addr::unchecked("some random address") - .to_string() - .try_into() - .unwrap(), // gateway address does not implement required interface - }, - ); - - assert!(res.is_ok()); - - let nonce: &mut usize = &mut 0; - let message = &generate_messages(ð, &polygon, nonce, 1)[0]; - - let res = config.connection_router.execute( - &mut config.app, - eth.gateway.clone(), - &ExecuteMsg::RouteMessages(vec![message.clone()]), - ); - assert!(res.is_err()); -} diff --git a/contracts/connection-router/tests/test_utils/mod.rs b/contracts/connection-router/tests/test_utils/mod.rs deleted file mode 100644 index 96b93932d..000000000 --- a/contracts/connection-router/tests/test_utils/mod.rs +++ /dev/null @@ -1,53 +0,0 @@ -use connection_router::{ - contract::{execute, instantiate, query}, - msg::InstantiateMsg, -}; -use connection_router_api::msg::{ExecuteMsg, QueryMsg}; -use cosmwasm_std::Addr; -use cw_multi_test::{App, ContractWrapper, Executor}; -use integration_tests::contract::Contract; - -#[derive(Clone)] -pub struct ConnectionRouterContract { - pub contract_addr: Addr, -} - -impl ConnectionRouterContract { - pub fn instantiate_contract(app: &mut App, admin: Addr, governance: Addr, nexus: Addr) -> Self { - let code = ContractWrapper::new(execute, instantiate, query); - let code_id = app.store_code(Box::new(code)); - - let contract_addr = app - .instantiate_contract( - code_id, - Addr::unchecked("router"), - &InstantiateMsg { - admin_address: admin.to_string(), - governance_address: governance.to_string(), - nexus_gateway: nexus.to_string(), - }, - &[], - "Contract", - None, - ) - .unwrap(); - - ConnectionRouterContract { contract_addr } - } -} - -impl Contract for ConnectionRouterContract { - type QMsg = QueryMsg; - type ExMsg = ExecuteMsg; - - fn contract_address(&self) -> Addr { - self.contract_addr.clone() - } -} - -pub fn are_contract_err_strings_equal( - actual: impl Into, - expected: impl Into, -) { - assert_eq!(actual.into().to_string(), expected.into().to_string()); -} diff --git a/integration-tests/tests/chain_freeze_unfreeze.rs b/integration-tests/tests/chain_freeze_unfreeze.rs new file mode 100644 index 000000000..94c122935 --- /dev/null +++ b/integration-tests/tests/chain_freeze_unfreeze.rs @@ -0,0 +1,93 @@ +use cosmwasm_std::{Addr, HexBinary}; + +use connection_router_api::{CrossChainId, Message}; +use integration_tests::contract::Contract; + +pub mod test_utils; + +// Tests that a chain can be frozen and unfrozen +#[test] +fn chain_can_be_freezed_unfreezed() { + let (mut protocol, chain1, chain2, workers, _) = test_utils::setup_test_case(); + + let msgs = vec![Message { + cc_id: CrossChainId { + chain: chain1.chain_name.clone(), + id: "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3" + .to_string() + .try_into() + .unwrap(), + }, + source_address: "0xBf12773B49()0e1Deb57039061AAcFA2A87DEaC9b9" + .to_string() + .try_into() + .unwrap(), + destination_address: "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + .to_string() + .try_into() + .unwrap(), + destination_chain: chain2.chain_name.clone(), + payload_hash: HexBinary::from_hex( + "3e50a012285f8e7ec59b558179cd546c55c477ebe16202aac7d7747e25be03be", + ) + .unwrap() + .as_slice() + .try_into() + .unwrap(), + }]; + let msg_ids: Vec = msgs.iter().map(|msg| msg.cc_id.clone()).collect(); + + // start the flow by submitting the message to the gateway + let (poll_id, expiry) = test_utils::verify_messages(&mut protocol.app, &chain1.gateway, &msgs); + + // do voting + test_utils::vote_success_for_all_messages( + &mut protocol.app, + &chain1.voting_verifier, + &msgs, + &workers, + poll_id, + ); + + test_utils::advance_at_least_to_height(&mut protocol.app, expiry); + + test_utils::end_poll(&mut protocol.app, &chain1.voting_verifier, poll_id); + + test_utils::route_messages(&mut protocol.app, &chain1.gateway, &msgs); + + test_utils::freeze_chain( + &mut protocol.app, + &protocol.connection_router, + &chain1.chain_name, + connection_router_api::GatewayDirection::Bidirectional, + &protocol.router_admin_address, + ); + + let response = chain1.gateway.execute( + &mut protocol.app, + Addr::unchecked("relayer"), + &gateway_api::msg::ExecuteMsg::RouteMessages(msgs.to_vec()), + ); + test_utils::assert_contract_err_strings_equal( + response.unwrap_err(), + connection_router_api::error::Error::ChainFrozen { + chain: chain1.chain_name.clone(), + }, + ); + + test_utils::unfreeze_chain( + &mut protocol.app, + &protocol.connection_router, + &chain1.chain_name, + connection_router_api::GatewayDirection::Bidirectional, + &protocol.router_admin_address, + ); + + // routed message should have been preserved + let found_msgs = + test_utils::get_messages_from_gateway(&mut protocol.app, &chain2.gateway, &msg_ids); + assert_eq!(found_msgs, msgs); + + // can route again + test_utils::route_messages(&mut protocol.app, &chain1.gateway, &msgs); +} diff --git a/integration-tests/tests/message_routing.rs b/integration-tests/tests/message_routing.rs index e61425c36..8954f9df3 100644 --- a/integration-tests/tests/message_routing.rs +++ b/integration-tests/tests/message_routing.rs @@ -1,6 +1,7 @@ -use cosmwasm_std::{HexBinary, Uint128}; +use cosmwasm_std::{Addr, HexBinary, Uint128}; use connection_router_api::{CrossChainId, Message}; +use integration_tests::contract::Contract; use crate::test_utils::AXL_DENOMINATION; @@ -109,3 +110,52 @@ fn single_message_can_be_verified_and_routed_and_proven_and_rewards_are_distribu assert_eq!(balance.amount, expected_rewards); } } + +#[test] +fn routing_to_incorrect_gateway_interface() { + let (mut protocol, chain1, chain2, _, _) = test_utils::setup_test_case(); + + let msgs = vec![Message { + cc_id: CrossChainId { + chain: chain1.chain_name.clone(), + id: "0x88d7956fd7b6fcec846548d83bd25727f2585b4be3add21438ae9fbb34625924-3" + .to_string() + .try_into() + .unwrap(), + }, + source_address: "0xBf12773B49()0e1Deb57039061AAcFA2A87DEaC9b9" + .to_string() + .try_into() + .unwrap(), + destination_address: "0xce16F69375520ab01377ce7B88f5BA8C48F8D666" + .to_string() + .try_into() + .unwrap(), + destination_chain: chain2.chain_name.clone(), + payload_hash: HexBinary::from_hex( + "3e50a012285f8e7ec59b558179cd546c55c477ebe16202aac7d7747e25be03be", + ) + .unwrap() + .as_slice() + .try_into() + .unwrap(), + }]; + + test_utils::upgrade_gateway( + &mut protocol.app, + &protocol.connection_router, + &protocol.governance_address, + &chain2.chain_name, + Addr::unchecked("some random address") + .to_string() + .try_into() + .unwrap(), // gateway address does not implement required interface, + ); + + let response = protocol.connection_router.execute( + &mut protocol.app, + chain1.gateway.contract_addr.clone(), + &connection_router_api::msg::ExecuteMsg::RouteMessages(msgs.to_vec()), + ); + assert!(response.is_err()) +} diff --git a/integration-tests/tests/test_utils/mod.rs b/integration-tests/tests/test_utils/mod.rs index f9a899858..abf6a7f60 100644 --- a/integration-tests/tests/test_utils/mod.rs +++ b/integration-tests/tests/test_utils/mod.rs @@ -3,7 +3,7 @@ use axelar_wasm_std::{ voting::{PollId, Vote}, Participant, Threshold, }; -use connection_router_api::{ChainName, CrossChainId, Message}; +use connection_router_api::{Address, ChainName, CrossChainId, GatewayDirection, Message}; use cosmwasm_std::{ coins, Addr, Attribute, BlockInfo, Event, HexBinary, StdError, Uint128, Uint256, Uint64, }; @@ -81,6 +81,60 @@ pub fn route_messages(app: &mut App, gateway: &GatewayContract, msgs: &[Message] assert!(response.is_ok()); } +pub fn freeze_chain( + app: &mut App, + router: &ConnectionRouterContract, + chain_name: &ChainName, + direction: GatewayDirection, + admin: &Addr, +) { + let response = router.execute( + app, + admin.clone(), + &connection_router_api::msg::ExecuteMsg::FreezeChain { + chain: chain_name.clone(), + direction, + }, + ); + assert!(response.is_ok(), "{:?}", response); +} + +pub fn unfreeze_chain( + app: &mut App, + router: &ConnectionRouterContract, + chain_name: &ChainName, + direction: GatewayDirection, + admin: &Addr, +) { + let response = router.execute( + app, + admin.clone(), + &connection_router_api::msg::ExecuteMsg::UnfreezeChain { + chain: chain_name.clone(), + direction, + }, + ); + assert!(response.is_ok(), "{:?}", response); +} + +pub fn upgrade_gateway( + app: &mut App, + router: &ConnectionRouterContract, + governance: &Addr, + chain_name: &ChainName, + contract_address: Address, +) { + let response = router.execute( + app, + governance.clone(), + &connection_router_api::msg::ExecuteMsg::UpgradeGateway { + chain: chain_name.clone(), + contract_address, + }, + ); + assert!(response.is_ok(), "{:?}", response); +} + pub fn vote_success_for_all_messages( app: &mut App, voting_verifier: &VotingVerifierContract, @@ -704,3 +758,10 @@ pub fn setup_test_case() -> (Protocol, Chain, Chain, Vec, Uint128) { let chain2 = setup_chain(&mut protocol, chains.get(1).unwrap().clone()); (protocol, chain1, chain2, workers, min_worker_bond) } + +pub fn assert_contract_err_strings_equal( + actual: impl Into, + expected: impl Into, +) { + assert_eq!(actual.into().to_string(), expected.into().to_string()); +}