diff --git a/relayer/cli/src/commands/query.rs b/relayer/cli/src/commands/query.rs index e601c60bdb..4e236bb9e6 100644 --- a/relayer/cli/src/commands/query.rs +++ b/relayer/cli/src/commands/query.rs @@ -32,6 +32,10 @@ pub enum QueryClientCmds { /// The `query client consensus` subcommand #[options(help = "query client consensus")] Consensus(client::QueryClientConsensusCmd), + + /// The `query client connections` subcommand + #[options(help = "query client connections")] + Connections(client::QueryClientConnectionsCmd), } #[derive(Command, Debug, Options, Runnable)] diff --git a/relayer/cli/src/commands/query/client.rs b/relayer/cli/src/commands/query/client.rs index e7afb2d5d0..7e25766c0b 100644 --- a/relayer/cli/src/commands/query/client.rs +++ b/relayer/cli/src/commands/query/client.rs @@ -2,17 +2,15 @@ use crate::prelude::*; use abscissa_core::{Command, Options, Runnable}; use relayer::config::{ChainConfig, Config}; -//use relayer::query::client::{query_client_consensus_state, query_client_full_state}; -//use relayer::query::{query, Request}; -//use relayer_modules::ics02_client::query::QueryClientFullState; -use relayer_modules::ics24_host::identifier::ClientId; - -//use crate::commands::utils::block_on; +use relayer::chain::Chain; use relayer::chain::CosmosSDKChain; use relayer_modules::ics24_host::error::ValidationError; +use relayer_modules::ics24_host::identifier::ClientId; +use relayer_modules::ics24_host::Path::ClientConnections; use tendermint::chain::Id as ChainId; +/// Query client state command #[derive(Clone, Command, Debug, Options)] pub struct QueryClientStateCmd { #[options(free, help = "identifier of the chain to query")] @@ -95,6 +93,7 @@ impl Runnable for QueryClientStateCmd { } } +/// Query client consensus command #[derive(Clone, Command, Debug, Options)] pub struct QueryClientConsensusCmd { #[options(free, help = "identifier of the chain to query")] @@ -214,9 +213,95 @@ fn validate_common_options( Ok((chain_config.clone(), client_id)) } +/// Query client connections command +#[derive(Clone, Command, Debug, Options)] +pub struct QueryClientConnectionsCmd { + #[options(free, help = "identifier of the chain to query")] + chain_id: Option, + + #[options(free, help = "identifier of the client to query")] + client_id: Option, + + #[options(help = "height of the state to query", short = "h")] + height: Option, + + #[options(help = "whether proof is required", short = "p")] + proof: Option, +} + +#[derive(Debug)] +struct QueryClientConnectionsOptions { + client_id: ClientId, + height: u64, + proof: bool, +} + +impl QueryClientConnectionsCmd { + fn validate_options( + &self, + config: &Config, + ) -> Result<(ChainConfig, QueryClientConnectionsOptions), String> { + let chain_id = self + .chain_id + .ok_or_else(|| "missing chain identifier".to_string())?; + let chain_config = config + .chains + .iter() + .find(|c| c.id == chain_id) + .ok_or_else(|| "missing chain configuration".to_string())?; + + let client_id = self + .client_id + .as_ref() + .ok_or_else(|| "missing client identifier".to_string())? + .parse() + .map_err(|err: ValidationError| err.to_string())?; + + let opts = QueryClientConnectionsOptions { + client_id, + height: match self.height { + Some(h) => h, + None => 0 as u64, + }, + proof: match self.proof { + Some(proof) => proof, + None => true, + }, + }; + Ok((chain_config.clone(), opts)) + } +} + +/// Command to handle query for client connections +/// To run without proof: +/// cargo run --bin relayer -- -c relayer/relay/tests/config/fixtures/relayer_conf_example.toml query client connections chain_A clientidone -h 4 -p false +impl Runnable for QueryClientConnectionsCmd { + fn run(&self) { + let config = app_config(); + + let (chain_config, opts) = match self.validate_options(&config) { + Err(err) => { + status_err!("invalid options: {}", err); + return; + } + Ok(result) => result, + }; + status_info!("Options", "{:?}", opts); + + let chain = CosmosSDKChain::from_config(chain_config).unwrap(); + let res = + chain.query::>(ClientConnections(opts.client_id), opts.height, opts.proof); + match res { + Ok(cs) => status_info!("client connections query result: ", "{:?}", cs), + Err(e) => status_info!("client connections query error", "{}", e), + } + } +} + +/// Tests #[cfg(test)] mod tests { - use crate::commands::query::client::QueryClientStateCmd; + use crate::commands::query::client::{QueryClientConnectionsCmd, QueryClientStateCmd}; use relayer::config::parse; #[test] @@ -304,4 +389,90 @@ mod tests { } } } + + #[test] + fn parse_query_client_connections_parameters() { + let default_params = QueryClientConnectionsCmd { + chain_id: Some("ibc0".to_string().parse().unwrap()), + client_id: Some("clientidone".to_string().parse().unwrap()), + height: Some(4), + proof: Some(false), + }; + + struct Test { + name: String, + params: QueryClientConnectionsCmd, + want_pass: bool, + } + + let tests: Vec = vec![ + Test { + name: "Good parameters".to_string(), + params: default_params.clone(), + want_pass: true, + }, + Test { + name: "No chain specified".to_string(), + params: QueryClientConnectionsCmd { + chain_id: None, + ..default_params.clone() + }, + want_pass: false, + }, + Test { + name: "Chain not configured".to_string(), + params: QueryClientConnectionsCmd { + chain_id: Some("notibc0oribc1".to_string().parse().unwrap()), + ..default_params.clone() + }, + want_pass: false, + }, + Test { + name: "No client id specified".to_string(), + params: QueryClientConnectionsCmd { + client_id: None, + ..default_params.clone() + }, + want_pass: false, + }, + Test { + name: "Bad client id, non-alpha".to_string(), + params: QueryClientConnectionsCmd { + client_id: Some("p34".to_string()), + ..default_params.clone() + }, + want_pass: false, + }, + ] + .into_iter() + .collect(); + + let path = concat!( + env!("CARGO_MANIFEST_DIR"), + "/tests/fixtures/two_chains.toml" + ); + + let config = parse(path).unwrap(); + + for test in tests { + let res = test.params.validate_options(&config); + + match res { + Ok(_res) => { + assert!( + test.want_pass, + "validate_options should have failed for test {}", + test.name + ); + } + Err(err) => { + assert!( + !test.want_pass, + "validate_options failed for test {}, \nerr {}", + test.name, err + ); + } + } + } + } }