Skip to content

Commit

Permalink
Add CCV chain bootstrap to CI with Neutron and Gaia (#3451)
Browse files Browse the repository at this point in the history
  • Loading branch information
ljoss17 authored and romac committed Jul 14, 2023
1 parent 49f8ac1 commit 84b59e0
Show file tree
Hide file tree
Showing 18 changed files with 761 additions and 4 deletions.
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

0 comments on commit 84b59e0

Please sign in to comment.