Skip to content

Commit

Permalink
add batching
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcobb23 committed Nov 6, 2024
1 parent 4c61487 commit fa0e3f6
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 58 deletions.
20 changes: 5 additions & 15 deletions contracts/interchain-token-service/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub enum Error {
#[error("failed to execute a cross-chain message")]
Execute,
#[error("failed to register chain")]
RegisterChain,
#[error("failed to register chains")]
RegisterChains,
#[error("failed to update chain")]
UpdateChain,
#[error("failed to freeze chain")]
Expand Down Expand Up @@ -97,19 +97,9 @@ pub fn execute(
payload,
}) => execute::execute_message(deps, cc_id, source_address, payload)
.change_context(Error::Execute),
ExecuteMsg::RegisterChain {
chain,
its_edge_contract,
max_uint,
max_target_decimals,
} => execute::register_chain(
deps,
chain,
its_edge_contract,
max_uint,
max_target_decimals,
)
.change_context(Error::RegisterChain),
ExecuteMsg::RegisterChains { chains } => {
execute::register_chains(deps, chains).change_context(Error::RegisterChains)
}
ExecuteMsg::UpdateChain {
chain,
its_edge_contract,
Expand Down
119 changes: 97 additions & 22 deletions contracts/interchain-token-service/src/contract/execute.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
use axelar_wasm_std::{killswitch, nonempty, FnExt, IntoContractError};
use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage, Uint256};
use error_stack::{bail, ensure, report, Result, ResultExt};
use itertools::Itertools;
use router_api::{Address, ChainNameRaw, CrossChainId};

use crate::events::Event;
use crate::primitives::HubMessage;
use crate::state::{self, is_chain_frozen, load_config, load_its_contract, TokenDeploymentType};
use crate::{
DeployInterchainToken, InterchainTransfer, Message, TokenConfig, TokenId, TokenInstance,
msg, DeployInterchainToken, InterchainTransfer, Message, TokenConfig, TokenId, TokenInstance,
};

#[derive(thiserror::Error, Debug, IntoContractError)]
pub enum Error {
#[error("unknown chain {0}")]
UnknownChain(ChainNameRaw),
#[error("chain not found {0}")]
ChainNotFound(ChainNameRaw),
#[error("unknown its address {0}")]
UnknownItsContract(Address),
#[error("failed to decode payload")]
Expand All @@ -40,8 +41,8 @@ pub enum Error {
},
#[error("state error")]
State,
#[error("chain config for {0} already set")]
ChainConfigAlreadySet(ChainNameRaw),
#[error("chain {0} already registered")]
ChainAlreadyRegistered(ChainNameRaw),
#[error("token {token_id} not deployed on chain {chain}")]
TokenNotDeployed {
token_id: TokenId,
Expand Down Expand Up @@ -107,7 +108,7 @@ fn execute_message_on_hub(
message: Message,
) -> Result<Response, Error> {
let destination_address = load_its_contract(deps.storage, &destination_chain)
.change_context_lazy(|| Error::UnknownChain(destination_chain.clone()))?;
.change_context_lazy(|| Error::ChainNotFound(destination_chain.clone()))?;

let message = apply_to_hub(
deps.storage,
Expand Down Expand Up @@ -177,7 +178,7 @@ fn ensure_is_its_source_address(
source_address: &Address,
) -> Result<(), Error> {
let source_its_contract = load_its_contract(storage, source_chain)
.change_context_lazy(|| Error::UnknownChain(source_chain.clone()))?;
.change_context_lazy(|| Error::ChainNotFound(source_chain.clone()))?;

ensure!(
source_address == &source_its_contract,
Expand Down Expand Up @@ -225,24 +226,36 @@ pub fn enable_execution(deps: DepsMut) -> Result<Response, Error> {
killswitch::disengage(deps.storage, Event::ExecutionEnabled).change_context(Error::State)
}

pub fn register_chains(deps: DepsMut, chains: Vec<msg::ChainConfig>) -> Result<Response, Error> {
chains
.into_iter()
.map(|chain_config| {
register_chain(
deps.storage,
chain_config.chain,
chain_config.its_edge_contract,
chain_config.max_uint,
chain_config.max_target_decimals,
)
})
.try_collect::<_, Vec<Response>, _>()?
.then(|_| Ok(Response::new()))
}

pub fn register_chain(
deps: DepsMut,
storage: &mut dyn Storage,
chain: ChainNameRaw,
its_address: Address,
max_uint: nonempty::Uint256,
max_target_decimals: u8,
) -> Result<Response, Error> {
match state::may_load_chain_config(deps.storage, &chain).change_context(Error::State)? {
Some(_) => bail!(Error::ChainConfigAlreadySet(chain)),
None => state::save_chain_config(
deps.storage,
&chain,
its_address,
max_uint,
max_target_decimals,
)
.change_context(Error::State)?
.then(|_| Ok(Response::new())),
match state::may_load_chain_config(storage, &chain).change_context(Error::State)? {
Some(_) => bail!(Error::ChainAlreadyRegistered(chain)),
None => {
state::save_chain_config(storage, &chain, its_address, max_uint, max_target_decimals)
.change_context(Error::State)?
.then(|_| Ok(Response::new()))
}
}
}

Expand Down Expand Up @@ -611,10 +624,10 @@ mod tests {

use crate::contract::execute::{
disable_execution, enable_execution, execute_message, freeze_chain, register_chain,
unfreeze_chain, Error,
register_chains, unfreeze_chain, update_chain, Error,
};
use crate::state::{self, Config};
use crate::{DeployInterchainToken, HubMessage, InterchainTransfer};
use crate::{msg, DeployInterchainToken, HubMessage, InterchainTransfer};

const SOLANA: &str = "solana";
const ETHEREUM: &str = "ethereum";
Expand Down Expand Up @@ -841,6 +854,68 @@ mod tests {
));
}

#[test]
fn register_chain_fails_if_already_registered() {
let mut deps = mock_dependencies();
assert_ok!(register_chain(
deps.as_mut().storage,
SOLANA.parse().unwrap(),
ITS_ADDRESS.to_string().try_into().unwrap(),
Uint256::one().try_into().unwrap(),
16u8
));
assert_err_contains!(
register_chain(
deps.as_mut().storage,
SOLANA.parse().unwrap(),
ITS_ADDRESS.to_string().try_into().unwrap(),
Uint256::one().try_into().unwrap(),
16u8
),
Error,
Error::ChainAlreadyRegistered(..)
);
}

#[test]
fn register_chains_fails_if_any_already_registered() {
let mut deps = mock_dependencies();
let chains = vec![
msg::ChainConfig {
chain: SOLANA.parse().unwrap(),
its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(),
max_uint: Uint256::MAX.try_into().unwrap(),
max_target_decimals: 16u8,
},
msg::ChainConfig {
chain: XRPL.parse().unwrap(),
its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(),
max_uint: Uint256::MAX.try_into().unwrap(),
max_target_decimals: 16u8,
},
];
assert_ok!(register_chains(deps.as_mut(), chains[0..1].to_vec()));
assert_err_contains!(
register_chains(deps.as_mut(), chains,),
Error,
Error::ChainAlreadyRegistered(..)
);
}

#[test]
fn update_chain_fails_if_not_registered() {
let mut deps = mock_dependencies();
assert_err_contains!(
update_chain(
deps.as_mut(),
SOLANA.parse().unwrap(),
ITS_ADDRESS.parse().unwrap()
),
Error,
Error::State
);
}

fn init(deps: &mut OwnedDeps<MemoryStorage, MockApi, MockQuerier>) {
assert_ok!(permission_control::set_admin(
deps.as_mut().storage,
Expand All @@ -866,7 +941,7 @@ mod tests {
for chain_name in [SOLANA, ETHEREUM, XRPL] {
let chain = ChainNameRaw::try_from(chain_name).unwrap();
assert_ok!(register_chain(
deps.as_mut(),
deps.as_mut().storage,
chain.clone(),
ITS_ADDRESS.to_string().try_into().unwrap(),
Uint256::one().try_into().unwrap(),
Expand Down
15 changes: 9 additions & 6 deletions contracts/interchain-token-service/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,7 @@ pub enum ExecuteMsg {
/// ITS Hub can send cross-chain messages to it, or receive messages from it.
/// If an ITS contract is already set for the chain, an error is returned.
#[permission(Governance)]
RegisterChain {
chain: ChainNameRaw,
its_edge_contract: Address,
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 another chain where smaller uint values are used
},
RegisterChains { chains: Vec<ChainConfig> },

#[permission(Governance)]
UpdateChain {
Expand All @@ -56,6 +51,14 @@ pub enum ExecuteMsg {
EnableExecution,
}

#[cw_serde]
pub struct ChainConfig {
pub chain: ChainNameRaw,
pub its_edge_contract: Address,
pub max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard
pub max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to another chain where smaller uint values are used
}

#[cw_serde]
#[derive(QueryResponses)]
pub enum QueryMsg {
Expand Down
59 changes: 51 additions & 8 deletions contracts/interchain-token-service/tests/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{HexBinary, Uint256};
use interchain_token_service::contract::{self, ExecuteError};
use interchain_token_service::events::Event;
use interchain_token_service::msg::ExecuteMsg;
use interchain_token_service::msg::{self, ExecuteMsg};
use interchain_token_service::{
DeployInterchainToken, HubMessage, InterchainTransfer, TokenId, TokenSupply,
};
use router_api::{Address, ChainName, ChainNameRaw, CrossChainId};
use serde_json::json;
use utils::{params, TestMessage};
use utils::{params, register_chains, TestMessage};

mod utils;

Expand Down Expand Up @@ -55,7 +55,7 @@ fn register_update_its_contract_succeeds() {
}

#[test]
fn reregistering_its_contract_fails() {
fn reregistering_same_chain_fails() {
let mut deps = mock_dependencies();
utils::instantiate_contract(deps.as_mut()).unwrap();

Expand All @@ -81,12 +81,12 @@ fn reregistering_its_contract_fails() {
u8::MAX
),
Error,
Error::RegisterChain
Error::RegisterChains
);
}

#[test]
fn deregistering_unknown_chain_fails() {
fn update_unknown_chain_fails() {
let mut deps = mock_dependencies();
utils::instantiate_contract(deps.as_mut()).unwrap();

Expand All @@ -105,6 +105,49 @@ fn deregistering_unknown_chain_fails() {
);
}

#[test]
fn register_multiple_chains_succeeds() {
let mut deps = mock_dependencies();
utils::instantiate_contract(deps.as_mut()).unwrap();
let chains: Vec<msg::ChainConfig> = (0..10)
.map(|i| msg::ChainConfig {
chain: i.to_string().parse().unwrap(),
its_edge_contract: i.to_string().parse().unwrap(),
max_target_decimals: 18u8,
max_uint: Uint256::MAX.try_into().unwrap(),
})
.collect();
assert_ok!(register_chains(deps.as_mut(), chains.clone()));

for chain in chains {
let res = assert_ok!(utils::query_its_contract(
deps.as_ref(),
chain.chain.clone()
));
assert_eq!(res, Some(chain.its_edge_contract));
}
}

#[test]
fn register_multiple_chains_fails_if_one_invalid() {
let mut deps = mock_dependencies();
utils::instantiate_contract(deps.as_mut()).unwrap();
let chains: Vec<msg::ChainConfig> = (0..10)
.map(|i| msg::ChainConfig {
chain: i.to_string().parse().unwrap(),
its_edge_contract: i.to_string().parse().unwrap(),
max_target_decimals: 18u8,
max_uint: Uint256::MAX.try_into().unwrap(),
})
.collect();
assert_ok!(register_chains(deps.as_mut(), chains[0..1].to_vec()));
assert_err_contains!(
register_chains(deps.as_mut(), chains.clone()),
Error,
Error::RegisterChains
);
}

#[test]
fn execute_hub_message_succeeds() {
let (
Expand Down Expand Up @@ -485,7 +528,7 @@ fn execute_message_when_unknown_chain_fails() {
source_its_contract.clone(),
hub_message.clone().abi_encode(),
);
assert_err_contains!(result, ExecuteError, ExecuteError::UnknownChain(chain) if chain == &source_its_chain);
assert_err_contains!(result, ExecuteError, ExecuteError::ChainNotFound(chain) if chain == &source_its_chain);

utils::register_chain(
deps.as_mut(),
Expand All @@ -502,7 +545,7 @@ fn execute_message_when_unknown_chain_fails() {
source_its_contract,
hub_message.abi_encode(),
);
assert_err_contains!(result, ExecuteError, ExecuteError::UnknownChain(chain) if chain == &destination_its_chain);
assert_err_contains!(result, ExecuteError, ExecuteError::ChainNotFound(chain) if chain == &destination_its_chain);
}

#[test]
Expand Down Expand Up @@ -793,7 +836,7 @@ fn set_chain_config_should_fail_if_chain_config_is_already_set() {
assert_err_contains!(
utils::register_chain(deps.as_mut(), chain, address, max_uint, decimals),
ExecuteError,
ExecuteError::ChainConfigAlreadySet(_)
ExecuteError::ChainAlreadyRegistered(_)
)
}

Expand Down
Loading

0 comments on commit fa0e3f6

Please sign in to comment.