Skip to content

Add ICA transfer test to interchain-security tests #3493

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ jobs:
test -p ibc-integration-test --features clean-workers --no-fail-fast -- \
--nocapture --test-threads=1 clean_workers::

interchain-security:
interchain-security-no-ica:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
Expand All @@ -415,6 +415,43 @@ jobs:
- package: neutron
command: neutrond
account_prefix: neutron
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v22
with:
install_url: https://nixos-nix-install-tests.cachix.org/serve/vij683ly7sl95nnhb67bdjjfabclr85m/install
install_options: '--tarball-url-prefix https://nixos-nix-install-tests.cachix.org/serve'
extra_nix_config: |
experimental-features = nix-command flakes
- uses: cachix/cachix-action@v12
with:
name: cosmos
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- uses: Swatinem/rust-cache@v1
- uses: actions-rs/cargo@v1
with:
command: test
args: -p ibc-integration-test --features interchain-security --no-fail-fast --no-run
- env:
RUST_LOG: info
RUST_BACKTRACE: 1
NO_COLOR_LOG: 1
CHAIN_COMMAND_PATHS: gaiad,${{ matrix.chain.command }}
ACCOUNT_PREFIXES: cosmos,${{ matrix.chain.account_prefix }}
run: |
nix shell .#gaia9 .#${{ matrix.chain.package }} -c cargo \
test -p ibc-integration-test --features interchain-security --no-fail-fast -- \
--nocapture --test-threads=1 interchain_security::

interchain-security-ica:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
chain:
- package: stride-consumer
command: strided
account_prefix: stride
Expand Down Expand Up @@ -446,7 +483,7 @@ jobs:
ACCOUNT_PREFIXES: cosmos,${{ matrix.chain.account_prefix }}
run: |
nix shell .#gaia9 .#${{ matrix.chain.package }} -c cargo \
test -p ibc-integration-test --features interchain-security --no-fail-fast -- \
test -p ibc-integration-test --features interchain-security,ica --no-fail-fast -- \
--nocapture --test-threads=1 interchain_security::

model-based-test:
Expand Down
83 changes: 18 additions & 65 deletions tools/integration-test/src/tests/ica.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,21 @@ use ibc_relayer::config::{
use ibc_relayer::event::IbcEventWithHeight;
use ibc_relayer_types::applications::ics27_ica::msgs::send_tx::MsgSendTx;
use ibc_relayer_types::applications::ics27_ica::packet_data::InterchainAccountPacketData;
use ibc_relayer_types::core::ics04_channel::version::Version;
use ibc_relayer_types::signer::Signer;
use ibc_relayer_types::{
applications::{
ics27_ica::{cosmos_tx::CosmosTx, msgs::register::MsgRegisterInterchainAccount},
transfer::{msgs::send::MsgSend, Amount, Coin},
},
bigint::U256,
core::ics04_channel::channel::State,
events::IbcEvent,
timestamp::Timestamp,
tx_msg::Msg,
use ibc_relayer_types::applications::{
ics27_ica::cosmos_tx::CosmosTx,
transfer::{msgs::send::MsgSend, Amount, Coin},
};

use ibc_test_framework::{
ibc::denom::Denom,
prelude::*,
relayer::channel::{assert_eventually_channel_established, query_channel_end},
use ibc_relayer_types::bigint::U256;
use ibc_relayer_types::core::ics04_channel::channel::State;
use ibc_relayer_types::signer::Signer;
use ibc_relayer_types::timestamp::Timestamp;
use ibc_relayer_types::tx_msg::Msg;

use ibc_test_framework::chain::ext::ica::register_interchain_account;
use ibc_test_framework::ibc::denom::Denom;
use ibc_test_framework::prelude::*;
use ibc_test_framework::relayer::channel::{
assert_eventually_channel_established, query_channel_end,
};

#[test]
Expand Down Expand Up @@ -97,7 +94,8 @@ impl BinaryConnectionTest for IcaFilterTestAllow {
) -> Result<(), Error> {
// Register an interchain account on behalf of
// controller wallet `user1` where the counterparty chain is the interchain accounts host.
let (wallet, channel_id, port_id) = register_interchain_account(&chains, &connection)?;
let (wallet, channel_id, port_id) =
register_interchain_account(&chains.node_a, chains.handle_a(), &connection)?;

// Check that the corresponding ICA channel is eventually established.
let _counterparty_channel_id = assert_eventually_channel_established(
Expand Down Expand Up @@ -230,7 +228,8 @@ impl BinaryConnectionTest for IcaFilterTestDeny {
) -> Result<(), Error> {
// Register an interchain account on behalf of controller wallet `user1`
// where the counterparty chain is the interchain accounts host.
let (_, channel_id, port_id) = register_interchain_account(&chains, &connection)?;
let (_, channel_id, port_id) =
register_interchain_account(&chains.node_a, chains.handle_a(), &connection)?;

// Wait a bit, the relayer will refuse to complete the channel handshake
// because the port is explicitly disallowed by the filter.
Expand All @@ -247,49 +246,3 @@ impl BinaryConnectionTest for IcaFilterTestDeny {
)
}
}

#[allow(clippy::type_complexity)]
fn register_interchain_account<ChainA: ChainHandle, ChainB: ChainHandle>(
chains: &ConnectedChains<ChainA, ChainB>,
connection: &ConnectedConnection<ChainA, ChainB>,
) -> Result<
(
MonoTagged<ChainA, Wallet>,
TaggedChannelId<ChainA, ChainB>,
TaggedPortId<ChainA, ChainB>,
),
Error,
> {
let wallet = chains.node_a.wallets().relayer().cloned();

let owner = chains.handle_a().get_signer()?;

let version_str = format!("{{\"version\":\"ics27-1\",\"encoding\":\"proto3\",\"tx_type\":\"sdk_multi_msg\",\"controller_connection_id\":\"{}\",\"host_connection_id\":\"{}\"}}", connection.connection_id_a.0, connection.connection_id_b.0);
let msg = MsgRegisterInterchainAccount {
owner,
connection_id: connection.connection_id_a.0.clone(),
version: Version::new(version_str),
};

let msg_any = msg.to_any();

let tm = TrackedMsgs::new_static(vec![msg_any], "RegisterInterchainAccount");

let events = chains
.handle_a()
.send_messages_and_wait_commit(tm)
.map_err(Error::relayer)?;

for event in events.iter() {
if let IbcEvent::OpenInitChannel(open_init) = &event.event {
let channel_id = open_init.channel_id.clone().ok_or(()).map_err(|_| Error::generic(eyre!("channel_id is empty in the event response after sending MsgRegisterInterchainAccount")))?;
return Ok((
wallet,
TaggedChannelId::new(channel_id),
TaggedPortId::new(open_init.port_id.clone()),
));
}
}

Err(Error::generic(eyre!("could not retrieve an OpenInitChannel event resonse after sending MsgRegisterInterchainAccount")))
}
188 changes: 188 additions & 0 deletions tools/integration-test/src/tests/interchain_security/ica_transfer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
//! The following tests are for the Interchain Security.
//! These tests require the first chain to be a Provider chain and
//! the second chain a Consumer chain.
use std::str::FromStr;

use ibc_relayer::chain::tracking::TrackedMsgs;
use ibc_relayer::event::IbcEventWithHeight;
use ibc_relayer_types::applications::ics27_ica::cosmos_tx::CosmosTx;
use ibc_relayer_types::applications::ics27_ica::msgs::send_tx::MsgSendTx;
use ibc_relayer_types::applications::ics27_ica::packet_data::InterchainAccountPacketData;
use ibc_relayer_types::applications::transfer::msgs::send::MsgSend;
use ibc_relayer_types::applications::transfer::{Amount, Coin};
use ibc_relayer_types::bigint::U256;
use ibc_relayer_types::signer::Signer;
use ibc_relayer_types::timestamp::Timestamp;
use ibc_relayer_types::tx_msg::Msg;
use ibc_test_framework::chain::config::set_voting_period;
use ibc_test_framework::chain::ext::ica::register_interchain_account;
use ibc_test_framework::framework::binary::channel::run_binary_interchain_security_channel_test;
use ibc_test_framework::prelude::*;
use ibc_test_framework::relayer::channel::assert_eventually_channel_established;

#[test]
fn test_ics_ica_transfer() -> Result<(), Error> {
run_binary_interchain_security_channel_test(&InterchainSecurityIcaTransferTest)
}

struct InterchainSecurityIcaTransferTest;

impl TestOverrides for InterchainSecurityIcaTransferTest {
fn modify_genesis_file(&self, genesis: &mut serde_json::Value) -> Result<(), Error> {
use serde_json::Value;

// Allow MsgSend messages over ICA
let allow_messages = genesis
.get_mut("app_state")
.and_then(|app_state| app_state.get_mut("interchainaccounts"))
.and_then(|ica| ica.get_mut("host_genesis_state"))
.and_then(|state| state.get_mut("params"))
.and_then(|params| params.get_mut("allow_messages"))
.and_then(|allow_messages| allow_messages.as_array_mut());

if let Some(allow_messages) = allow_messages {
allow_messages.push(Value::String("/cosmos.bank.v1beta1.MsgSend".to_string()));
} else {
return Err(Error::generic(eyre!("failed to update genesis file")));
}

// Consumer chain doesn't have a gov key.
if genesis
.get_mut("app_state")
.and_then(|app_state| app_state.get("gov"))
.is_some()
{
set_voting_period(genesis, "10s")?;
}
Ok(())
}

// The `ccv_consumer_chain` must be `true` for the Consumer chain.
// The `trusting_period` must be strictly smaller than the `unbonding_period`
// specified in the Consumer chain proposal. The test framework uses 100s in
// the proposal.
fn modify_relayer_config(&self, config: &mut Config) {
config.mode.channels.enabled = true;

for chain_config in config.chains.iter_mut() {
if chain_config.id == ChainId::from_string("ibcconsumer") {
chain_config.ccv_consumer_chain = true;
chain_config.trusting_period = Some(Duration::from_secs(99));
}
}
}
}

impl BinaryChannelTest for InterchainSecurityIcaTransferTest {
fn run<ChainA: ChainHandle, ChainB: ChainHandle>(
&self,
_config: &TestConfig,
_relayer: RelayerDriver,
chains: ConnectedChains<ChainA, ChainB>,
channel: ConnectedChannel<ChainA, ChainB>,
) -> Result<(), Error> {
let connection_b_to_a = channel.connection.clone().flip();
let (wallet, channel_id, port_id) =
register_interchain_account(&chains.node_b, chains.handle_b(), &connection_b_to_a)?;

// Check that the corresponding ICA channel is eventually established.
let _counterparty_channel_id = assert_eventually_channel_established(
chains.handle_b(),
chains.handle_a(),
&channel_id.as_ref(),
&port_id.as_ref(),
)?;

// Query the controller chain for the address of the ICA wallet on the host chain.
let ica_address = chains.node_b.chain_driver().query_interchain_account(
&wallet.address(),
&channel.connection.connection_id_b.as_ref(),
)?;

let stake_denom: MonoTagged<ChainA, Denom> = MonoTagged::new(Denom::base("stake"));

chains.node_a.chain_driver().assert_eventual_wallet_amount(
&ica_address.as_ref(),
&stake_denom.with_amount(0u64).as_ref(),
)?;

// Send funds to the interchain account.
let ica_fund = 42000u64;

chains.node_a.chain_driver().local_transfer_token(
&chains.node_a.wallets().user1(),
&ica_address.as_ref(),
&stake_denom.with_amount(ica_fund).as_ref(),
)?;

chains.node_a.chain_driver().assert_eventual_wallet_amount(
&ica_address.as_ref(),
&stake_denom.with_amount(ica_fund).as_ref(),
)?;

let amount = 12345;

let msg = MsgSend {
from_address: ica_address.to_string(),
to_address: chains.node_a.wallets().user2().address().to_string(),
amount: vec![Coin {
denom: stake_denom.to_string(),
amount: Amount(U256::from(amount)),
}],
};

let raw_msg = msg.to_any();

let cosmos_tx = CosmosTx {
messages: vec![raw_msg],
};

let raw_cosmos_tx = cosmos_tx.to_any();

let interchain_account_packet_data = InterchainAccountPacketData::new(raw_cosmos_tx.value);

let signer = Signer::from_str(&wallet.address().to_string()).unwrap();

// Send funds from the ICA account to the `user2` account on the host chain on behalf
// of the `user1` account on the controller chain.
let ica_events = interchain_send_tx(
chains.handle_b(),
&signer,
&channel.connection.connection_id_b.0,
interchain_account_packet_data,
Timestamp::from_nanoseconds(120000000000).unwrap(),
)?;

info!("ICA events from `CosmosTx`: {ica_events:#?}");

// Check that the ICA account's balance has been debited the sent amount.
chains.node_a.chain_driver().assert_eventual_wallet_amount(
&ica_address.as_ref(),
&stake_denom.with_amount(ica_fund - amount).as_ref(),
)?;
Ok(())
}
}

fn interchain_send_tx<ChainA: ChainHandle>(
chain: &ChainA,
from: &Signer,
connection: &ConnectionId,
msg: InterchainAccountPacketData,
relative_timeout: Timestamp,
) -> Result<Vec<IbcEventWithHeight>, Error> {
let msg = MsgSendTx {
owner: from.clone(),
connection_id: connection.clone(),
packet_data: msg,
relative_timeout,
};

let msg_any = msg.to_any();

let tm = TrackedMsgs::new_static(vec![msg_any], "SendTx");

chain
.send_messages_and_wait_commit(tm)
.map_err(Error::relayer)
}
3 changes: 3 additions & 0 deletions tools/integration-test/src/tests/interchain_security/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#[cfg(any(doc, feature = "ica"))]
pub mod ica_transfer;
pub mod simple_transfer;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! The following tests are for the Interchain Security.
//! These tests require the first chain to be a Producer chain and
//! These tests require the first chain to be a Provider chain and
//! the second chain a Consumer chain.
use ibc_test_framework::chain::config::set_voting_period;
use ibc_test_framework::framework::binary::channel::run_binary_interchain_security_channel_test;
Expand All @@ -8,12 +8,12 @@ use ibc_test_framework::util::random::random_u128_range;

#[test]
fn test_ics_transfer() -> Result<(), Error> {
run_binary_interchain_security_channel_test(&InterchainSecurityTest)
run_binary_interchain_security_channel_test(&InterchainSecurityTransferTest)
}

struct InterchainSecurityTest;
struct InterchainSecurityTransferTest;

impl TestOverrides for InterchainSecurityTest {
impl TestOverrides for InterchainSecurityTransferTest {
fn modify_genesis_file(&self, genesis: &mut serde_json::Value) -> Result<(), Error> {
// Consumer chain doesn't have a gov key.
if genesis
Expand All @@ -40,7 +40,7 @@ impl TestOverrides for InterchainSecurityTest {
}
}

impl BinaryChannelTest for InterchainSecurityTest {
impl BinaryChannelTest for InterchainSecurityTransferTest {
fn run<ChainA: ChainHandle, ChainB: ChainHandle>(
&self,
_config: &TestConfig,
Expand Down
Loading