Skip to content

Commit

Permalink
Merge branch 'main' into its_hub_freeze_per_chain
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcobb23 committed Oct 30, 2024
2 parents 514fd86 + a6952bb commit 621d42e
Show file tree
Hide file tree
Showing 8 changed files with 306 additions and 13 deletions.
22 changes: 21 additions & 1 deletion contracts/interchain-token-service/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::fmt::Debug;

use axelar_wasm_std::error::ContractError;
use axelar_wasm_std::{address, permission_control, FnExt, IntoContractError};
use axelar_wasm_std::{address, killswitch, permission_control, FnExt, IntoContractError};
use axelarnet_gateway::AxelarExecutableMsg;
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
Expand Down Expand Up @@ -34,10 +34,16 @@ pub enum Error {
FreezeChain,
#[error("failed to unfreeze chain")]
UnfreezeChain,
#[error("failed to set chain config")]
SetChainConfig,
#[error("failed to query its address")]
QueryItsContract,
#[error("failed to query all its addresses")]
QueryAllItsContracts,
#[error("failed to disable execution")]
DisableExecution,
#[error("failed to enable execution")]
EnableExecution,
}

#[cfg_attr(not(feature = "library"), entry_point)]
Expand Down Expand Up @@ -73,6 +79,8 @@ pub fn instantiate(
state::save_its_contract(deps.storage, chain, address)?;
}

killswitch::init(deps.storage, killswitch::State::Disengaged)?;

Ok(Response::new().add_events(
msg.its_contracts
.into_iter()
Expand Down Expand Up @@ -108,6 +116,18 @@ pub fn execute(
ExecuteMsg::UnfreezeChain { chain } => {
unfreeze_chain(deps, chain).change_context(Error::UnfreezeChain)
}
ExecuteMsg::DisableExecution => {
execute::disable_execution(deps).change_context(Error::DisableExecution)
}
ExecuteMsg::EnableExecution => {
execute::enable_execution(deps).change_context(Error::EnableExecution)
}
ExecuteMsg::SetChainConfig {
chain,
max_uint,
max_target_decimals,
} => execute::set_chain_config(deps, chain, max_uint, max_target_decimals)
.change_context(Error::SetChainConfig),
}?
.then(Ok)
}
Expand Down
102 changes: 93 additions & 9 deletions contracts/interchain-token-service/src/contract/execute.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use axelar_wasm_std::IntoContractError;
use axelar_wasm_std::{killswitch, nonempty, FnExt, IntoContractError};
use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage};
use error_stack::{bail, ensure, report, Result, ResultExt};
use router_api::{Address, ChainName, ChainNameRaw, CrossChainId};
Expand All @@ -21,12 +21,20 @@ pub enum Error {
FailedItsContractRegistration(ChainNameRaw),
#[error("failed to deregister its contract for chain {0}")]
FailedItsContractDeregistration(ChainNameRaw),
#[error("failed to query nexus")]
NexusQueryError,
#[error("state error")]
StateError,
#[error("failed to execute message")]
FailedExecuteMessage,
#[error("execution is currently disabled")]
ExecutionDisabled,
#[error("chain config for {0} already set")]
ChainConfigAlreadySet(ChainNameRaw),
#[error("failed to load chain config for chain {0}")]
LoadChainConfig(ChainNameRaw),
#[error("failed to save chain config for chain {0}")]
SaveChainConfig(ChainNameRaw),
#[error("chain {0} is frozen")]
ChainFrozen(ChainNameRaw),
#[error("state error")]
State,
}

/// Executes an incoming ITS message.
Expand All @@ -40,9 +48,13 @@ pub fn execute_message(
source_address: Address,
payload: HexBinary,
) -> Result<Response, Error> {
ensure!(
killswitch::is_contract_active(deps.storage),
Error::ExecutionDisabled
);
ensure_its_source_address(deps.storage, &cc_id.source_chain, &source_address)?;
ensure!(
!is_chain_frozen(deps.storage, &cc_id.source_chain).change_context(Error::StateError)?,
!is_chain_frozen(deps.storage, &cc_id.source_chain).change_context(Error::State)?,
Error::ChainFrozen(cc_id.source_chain)
);

Expand All @@ -53,7 +65,7 @@ pub fn execute_message(
} => {
ensure!(
!is_chain_frozen(deps.storage, &destination_chain)
.change_context(Error::StateError)?,
.change_context(Error::State)?,
Error::ChainFrozen(destination_chain)
);
let destination_address = load_its_contract(deps.storage, &destination_chain)
Expand Down Expand Up @@ -143,7 +155,7 @@ pub fn deregister_its_contract(deps: DepsMut, chain: ChainNameRaw) -> Result<Res
}

pub fn freeze_chain(deps: DepsMut, chain: ChainNameRaw) -> Result<Response, Error> {
state::freeze_chain(deps.storage, &chain).change_context(Error::StateError)?;
state::freeze_chain(deps.storage, &chain).change_context(Error::State)?;

Ok(Response::new())
}
Expand All @@ -154,6 +166,31 @@ pub fn unfreeze_chain(deps: DepsMut, chain: ChainNameRaw) -> Result<Response, Er
Ok(Response::new())
}

pub fn disable_execution(deps: DepsMut) -> Result<Response, Error> {
killswitch::engage(deps.storage, Event::ExecutionDisabled).change_context(Error::State)
}

pub fn enable_execution(deps: DepsMut) -> Result<Response, Error> {
killswitch::disengage(deps.storage, Event::ExecutionEnabled).change_context(Error::State)
}


pub fn set_chain_config(
deps: DepsMut,
chain: ChainNameRaw,
max_uint: nonempty::Uint256,
max_target_decimals: u8,
) -> Result<Response, Error> {
match state::may_load_chain_config(deps.storage, &chain)
.change_context_lazy(|| Error::LoadChainConfig(chain.clone()))?
{
Some(_) => bail!(Error::ChainConfigAlreadySet(chain)),
None => state::save_chain_config(deps.storage, &chain, max_uint, max_target_decimals)
.change_context_lazy(|| Error::SaveChainConfig(chain))?
.then(|_| Ok(Response::new())),
}
}

#[cfg(test)]
mod tests {
use assert_ok::assert_ok;
Expand All @@ -164,7 +201,7 @@ mod tests {
use router_api::{ChainNameRaw, CrossChainId};

use crate::contract::execute::{
execute_message, freeze_chain, register_its_contract, unfreeze_chain, Error,
disable_execution, enable_execution, execute_message, freeze_chain, register_its_contract, unfreeze_chain, Error
};
use crate::state::{self, Config};
use crate::{HubMessage, Message};
Expand All @@ -186,6 +223,53 @@ mod tests {
.unwrap()
}

#[test]
fn should_be_able_to_disable_and_enable_execution() {
let mut deps = mock_dependencies();
init(&mut deps);

assert_ok!(disable_execution(deps.as_mut()));

let msg = HubMessage::SendToHub {
destination_chain: ChainNameRaw::try_from(SOLANA).unwrap(),
message: Message::InterchainTransfer {
token_id: [7u8; 32].into(),
source_address: its_address(),
destination_address: its_address(),
amount: Uint256::one().try_into().unwrap(),
data: None,
},
};
let res = execute_message(
deps.as_mut(),
CrossChainId {
source_chain: ChainNameRaw::try_from(SOLANA).unwrap(),
message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32)
.to_string()
.try_into()
.unwrap(),
},
ITS_ADDRESS.to_string().try_into().unwrap(),
msg.clone().abi_encode(),
);
assert_err_contains!(res, Error, Error::ExecutionDisabled);

assert_ok!(enable_execution(deps.as_mut()));

assert_ok!(execute_message(
deps.as_mut(),
CrossChainId {
source_chain: ChainNameRaw::try_from(SOLANA).unwrap(),
message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32)
.to_string()
.try_into()
.unwrap(),
},
ITS_ADDRESS.to_string().try_into().unwrap(),
msg.abi_encode(),
));
}

#[test]
fn execution_should_fail_if_source_chain_is_frozen() {
let mut deps = mock_dependencies();
Expand Down
4 changes: 4 additions & 0 deletions contracts/interchain-token-service/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub enum Event {
ItsContractDeregistered {
chain: ChainNameRaw,
},
ExecutionDisabled,
ExecutionEnabled,
}

impl From<Event> for cosmwasm_std::Event {
Expand All @@ -35,6 +37,8 @@ impl From<Event> for cosmwasm_std::Event {
cosmwasm_std::Event::new("its_contract_deregistered")
.add_attribute("chain", chain.to_string())
}
Event::ExecutionDisabled => cosmwasm_std::Event::new("execution_disabled"),
Event::ExecutionEnabled => cosmwasm_std::Event::new("execution_enabled"),
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions contracts/interchain-token-service/src/msg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::collections::HashMap;

use axelar_wasm_std::nonempty;
use axelarnet_gateway::AxelarExecutableMsg;
use cosmwasm_schema::{cw_serde, QueryResponses};
use msgs_derive::EnsurePermissions;
Expand Down Expand Up @@ -41,6 +42,19 @@ pub enum ExecuteMsg {
/// Unfreeze execution of ITS messages for a particular chain
#[permission(Elevated)]
UnfreezeChain { chain: ChainNameRaw },

#[permission(Elevated)]
DisableExecution,

#[permission(Elevated)]
EnableExecution,
/// Set the chain configuration for a chain.
#[permission(Governance)]
SetChainConfig {
chain: ChainNameRaw,
max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard
max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to anothe chain where smaller uint values are used
},
}

#[cw_serde]
Expand Down
32 changes: 30 additions & 2 deletions contracts/interchain-token-service/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ pub enum Error {
ItsContractNotFound(ChainNameRaw),
#[error("its address for chain {0} already registered")]
ItsContractAlreadyRegistered(ChainNameRaw),
#[error("gateway token already registered {0}")]
GatewayTokenAlreadyRegistered(nonempty::String),
// This is a generic error to use when cw_storage_plus returns an error that is unexpected and
// should never happen, such as an error encountered when saving data.
#[error("storage error")]
Expand All @@ -28,9 +26,16 @@ pub struct Config {
pub axelarnet_gateway: Addr,
}

#[cw_serde]
pub struct ChainConfig {
max_uint: nonempty::Uint256,
max_target_decimals: u8,
}

const CONFIG: Item<Config> = Item::new("config");
const ITS_CONTRACTS: Map<&ChainNameRaw, Address> = Map::new("its_contracts");
const FROZEN_CHAINS: Map<&ChainNameRaw, ()> = Map::new("frozen_chains");
const CHAIN_CONFIGS: Map<&ChainNameRaw, ChainConfig> = Map::new("chain_configs");

pub fn load_config(storage: &dyn Storage) -> Config {
CONFIG
Expand All @@ -42,6 +47,29 @@ pub fn save_config(storage: &mut dyn Storage, config: &Config) -> Result<(), Err
CONFIG.save(storage, config).change_context(Error::Storage)
}

pub fn may_load_chain_config(
storage: &dyn Storage,
chain: &ChainNameRaw,
) -> Result<Option<ChainConfig>, Error> {
CHAIN_CONFIGS.may_load(storage, chain).change_context(Error::Storage)
}

pub fn save_chain_config(
storage: &mut dyn Storage,
chain: &ChainNameRaw,
max_uint: nonempty::Uint256,
max_target_decimals: u8,
) -> Result<(), Error> {
CHAIN_CONFIGS.save(
storage,
chain,
&ChainConfig {
max_uint,
max_target_decimals,
},
).change_context(Error::Storage)
}

pub fn may_load_its_contract(
storage: &dyn Storage,
chain: &ChainNameRaw,
Expand Down
Loading

0 comments on commit 621d42e

Please sign in to comment.