Skip to content
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

Add CCV chain bootstrap to CI with Neutron and Gaia #3451

Merged
merged 6 commits into from
Jun 30, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Add a new trait `InterchainSecurityChainTest` and two functions
`run_binary_interchain_security_node_test` and `run_binary_interchain_security_channel_test`
which can be used to bootstrap a Provider and Consumer chain for integration tests.
Add a CI job to run tests with Gaia as a provider chain and Neutron as a Consumer chain.
([\#3450](https://github.com/informalsystems/hermes/issues/3450))
([\#3387](https://github.com/informalsystems/hermes/issues/3387))
40 changes: 40 additions & 0 deletions .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,46 @@ jobs:
test -p ibc-integration-test --features clean-workers --no-fail-fast -- \
--nocapture --test-threads=1 clean_workers::

interchain-security:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
chain:
- 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::

model-based-test:
runs-on: ubuntu-20.04
timeout-minutes: 60
Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
stride
stride-no-admin
migaloo
neutron
;

python = nixpkgs.python3.withPackages (p: [
Expand Down
1 change: 1 addition & 0 deletions tools/integration-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ forward-packet = []
ics31 = []
clean-workers = []
fee-grant = []
interchain-security = []

[[bin]]
name = "test_setup_with_binary_channel"
Expand Down
150 changes: 150 additions & 0 deletions tools/integration-test/src/tests/interchain_security.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
//! The following tests are for the Interchain Security.
//! These tests require the first chain to be a Producer 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;
use ibc_test_framework::prelude::*;
use ibc_test_framework::util::random::random_u128_range;

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

struct InterchainSecurityTest;

impl TestOverrides for InterchainSecurityTest {
fn modify_genesis_file(&self, genesis: &mut serde_json::Value) -> Result<(), Error> {
// 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) {
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 InterchainSecurityTest {
fn run<ChainA: ChainHandle, ChainB: ChainHandle>(
&self,
_config: &TestConfig,
_relayer: RelayerDriver,
chains: ConnectedChains<ChainA, ChainB>,
channel: ConnectedChannel<ChainA, ChainB>,
) -> Result<(), Error> {
let denom_a = chains.node_a.denom();

let wallet_a = chains.node_a.wallets().user1().cloned();
let wallet_b = chains.node_b.wallets().user1().cloned();
let wallet_c = chains.node_a.wallets().user2().cloned();

let balance_a = chains
.node_a
.chain_driver()
.query_balance(&wallet_a.address(), &denom_a)?;

let a_to_b_amount = random_u128_range(1000, 5000);

info!(
"Sending IBC transfer from chain {} to chain {} with: channel id {}, port id {} and amount of {} {}",
chains.chain_id_a(),
chains.chain_id_b(),
channel.channel_id_a.to_string(),
channel.port_a.to_string(),
a_to_b_amount,
denom_a
);

chains.node_a.chain_driver().ibc_transfer_token(
&channel.port_a.as_ref(),
&channel.channel_id_a.as_ref(),
&wallet_a.as_ref(),
&wallet_b.address(),
&denom_a.with_amount(a_to_b_amount).as_ref(),
)?;

let denom_b = derive_ibc_denom(
&channel.port_b.as_ref(),
&channel.channel_id_b.as_ref(),
&denom_a,
)?;

info!(
"Waiting for user on chain B to receive IBC transferred amount of {}",
a_to_b_amount
);

chains.node_a.chain_driver().assert_eventual_wallet_amount(
&wallet_a.address(),
&(balance_a - a_to_b_amount).as_ref(),
)?;

chains.node_b.chain_driver().assert_eventual_wallet_amount(
&wallet_b.address(),
&denom_b.with_amount(a_to_b_amount).as_ref(),
)?;

info!(
"successfully performed IBC transfer from chain {} to chain {}",
chains.chain_id_a(),
chains.chain_id_b(),
);

let balance_c = chains
.node_a
.chain_driver()
.query_balance(&wallet_c.address(), &denom_a)?;

let b_to_a_amount = random_u128_range(500, a_to_b_amount);

info!(
"Sending IBC transfer from chain {} to chain {} with: channel id {}, port id {} and amount of {}",
chains.chain_id_b(),
chains.chain_id_a(),
channel.channel_id_b.to_string(),
channel.port_b.to_string(),
b_to_a_amount,
);

chains.node_b.chain_driver().ibc_transfer_token(
&channel.port_b.as_ref(),
&channel.channel_id_b.as_ref(),
&wallet_b.as_ref(),
&wallet_c.address(),
&denom_b.with_amount(b_to_a_amount).as_ref(),
)?;

chains.node_b.chain_driver().assert_eventual_wallet_amount(
&wallet_b.address(),
&denom_b.with_amount(a_to_b_amount - b_to_a_amount).as_ref(),
)?;

chains.node_a.chain_driver().assert_eventual_wallet_amount(
&wallet_c.address(),
&(balance_c + b_to_a_amount).as_ref(),
)?;

info!(
"successfully performed reverse IBC transfer from chain {} back to chain {}",
chains.chain_id_b(),
chains.chain_id_a(),
);
Ok(())
}
}
3 changes: 3 additions & 0 deletions tools/integration-test/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,6 @@ pub mod clean_workers;

#[cfg(any(doc, feature = "fee-grant"))]
pub mod fee_grant;

#[cfg(any(doc, feature = "interchain-security"))]
pub mod interchain_security;
6 changes: 5 additions & 1 deletion tools/test-framework/src/bootstrap/binary/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use ibc_relayer::foreign_client::{
use ibc_relayer::keyring::errors::ErrorDetail as KeyringErrorDetail;
use ibc_relayer::registry::SharedRegistry;
use ibc_relayer_types::core::ics24_host::identifier::ClientId;
use std::fs;
use std::path::Path;
use std::time::Duration;
use std::{fs, thread};
use tracing::{debug, info};

use crate::relayer::driver::RelayerDriver;
Expand Down Expand Up @@ -72,6 +73,9 @@ pub fn bootstrap_chains_with_full_nodes(
let handle_a = spawn_chain_handle(|| {}, &registry, &node_a)?;
let handle_b = spawn_chain_handle(|| {}, &registry, &node_b)?;

// Wait for the chain handles to be spawned
thread::sleep(Duration::from_secs(10));

pad_client_ids(&handle_a, &handle_b, options.pad_client_id_a_to_b)?;
pad_client_ids(&handle_b, &handle_a, options.pad_client_id_b_to_a)?;

Expand Down
136 changes: 136 additions & 0 deletions tools/test-framework/src/bootstrap/consumer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*!
Helper functions for bootstrapping a consumer full node.
*/
use eyre::eyre;
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::Duration;
use toml;
use tracing::info;

use crate::chain::builder::ChainBuilder;
use crate::chain::config;
use crate::chain::ext::bootstrap::ChainBootstrapMethodsExt;
use crate::error::Error;
use crate::prelude::{ChainDriver, Denom, FullNode, TestWallets, Token};
use crate::util::random::random_u128_range;

pub fn bootstrap_consumer_node(
builder: &ChainBuilder,
prefix: &str,
node_a: &FullNode,
config_modifier: impl FnOnce(&mut toml::Value) -> Result<(), Error>,
genesis_modifier: impl FnOnce(&mut serde_json::Value) -> Result<(), Error>,
chain_number: usize,
provider_chain_driver: &ChainDriver,
) -> Result<FullNode, Error> {
let stake_denom = Denom::base("stake");

let denom = Denom::base("samoleans");

let initial_amount = random_u128_range(1_000_000_000_000_000_000, 2_000_000_000_000_000_000);

let initial_stake = Token::new(stake_denom, initial_amount);
let additional_initial_stake = initial_stake
.clone()
.checked_add(1_000_000_000_000u64)
.ok_or(Error::generic(eyre!(
"error creating initial stake with additional amount"
)))?;

let initial_coin = Token::new(denom.clone(), initial_amount);
let chain_driver = builder.new_chain(prefix, false, chain_number)?;

chain_driver.initialize()?;

let validator = chain_driver.add_wallet("validator")?;
let relayer = chain_driver.add_wallet("relayer")?;
let user1 = chain_driver.add_wallet("user1")?;
let user2 = chain_driver.add_wallet("user2")?;

chain_driver.add_genesis_account(&validator.address, &[&additional_initial_stake])?;
chain_driver.add_genesis_account(&relayer.address, &[&initial_stake, &initial_coin])?;
chain_driver.add_genesis_account(&user1.address, &[&initial_stake, &initial_coin])?;
chain_driver.add_genesis_account(&user2.address, &[&initial_stake, &initial_coin])?;

// Wait for the consumer chain to be initialized before querying the genesis
thread::sleep(Duration::from_secs(10));

node_a
.chain_driver
.query_consumer_genesis(&chain_driver, chain_driver.chain_id.as_str())?;

chain_driver.replace_genesis_state()?;

chain_driver.update_genesis_file("genesis.json", genesis_modifier)?;
// The configuration `soft_opt_out_threshold` might be missing and is required
// for chains such as Neutron
chain_driver.update_genesis_file("genesis.json", |genesis| {
config::set_soft_opt_out_threshold(genesis, "0.05")?;
Ok(())
})?;

let log_level = std::env::var("CHAIN_LOG_LEVEL").unwrap_or_else(|_| "info".to_string());

chain_driver.update_chain_config("config.toml", |config| {
config::set_log_level(config, &log_level)?;
config::set_rpc_port(config, chain_driver.rpc_port)?;
config::set_p2p_port(config, chain_driver.p2p_port)?;
config::set_pprof_port(config, chain_driver.pprof_port)?;
config::set_timeout_commit(config, Duration::from_secs(1))?;
config::set_timeout_propose(config, Duration::from_secs(1))?;
config::set_mode(config, "validator")?;

config_modifier(config)?;

Ok(())
})?;

chain_driver.update_chain_config("app.toml", |config| {
config::set_grpc_port(config, chain_driver.grpc_port)?;
config::disable_grpc_web(config)?;
config::disable_api(config)?;
config::set_minimum_gas_price(config, "0stake")?;

Ok(())
})?;

chain_driver.copy_validator_key_pair(provider_chain_driver)?;

let process = chain_driver.start()?;

info!(
"started new chain {} at with home path {} and RPC address {}.",
chain_driver.chain_id,
chain_driver.home_path,
chain_driver.rpc_address(),
);

info!(
"user wallet for chain {} - id: {}, address: {}",
chain_driver.chain_id, user1.id.0, user1.address.0,
);

info!(
"you can manually interact with the chain using commands starting with:\n{} --home '{}' --node {}",
chain_driver.command_path,
chain_driver.home_path,
chain_driver.rpc_address(),
);

let wallets = TestWallets {
validator,
relayer,
user1,
user2,
};

let node = FullNode {
chain_driver,
denom,
wallets,
process: Arc::new(RwLock::new(process)),
};

Ok(node)
}
1 change: 1 addition & 0 deletions tools/test-framework/src/bootstrap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

pub mod binary;
pub mod consumer;
pub mod init;
pub mod nary;
pub mod single;
Loading