From 1e5f2dc6d1a0c88aa4fb4c4a9138bf1ca7f93fee Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 13 Jan 2021 16:04:25 +0200 Subject: [PATCH] CLI Command to query connection channels (#506) * Implemented the 'query connection channels' command * Avoid calls to unwrap * Updated the changelog * Added basic CLI test * Fix FMT * Added JSON output option * Overrriding default tracing cfg [WIP] * Revert "Overrriding default tracing cfg [WIP]" This reverts commit fb2e270183b82efd7a59d94abda3bc5f43093216. * Simplified output, removed JSON * Fixed output text * Using find_chain method * Added TODO addressing Vitor's comment --- CHANGELOG.md | 4 + relayer-cli/src/commands/query.rs | 4 + relayer-cli/src/commands/query/connection.rs | 172 ++++++++++++++++++- relayer/src/chain.rs | 10 +- relayer/src/chain/cosmos.rs | 41 ++++- relayer/src/chain/mock.rs | 13 +- 6 files changed, 230 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78d752a2d2..847fce5c4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,20 @@ ### FEATURES - Add support for streamlining releases ([#507]) +- [relayer-cli] + - Implement command to query the channels associated with a connection ([#505]) ### IMPROVEMENTS - Update to `tendermint-rs` v0.17.1 ([#517]) +[#505]: https://github.com/informalsystems/ibc-rs/issues/505 [#507]: https://github.com/informalsystems/ibc-rs/issues/507 [#511]: https://github.com/informalsystems/ibc-rs/pull/511 [#517]: https://github.com/informalsystems/ibc-rs/issues/517 + ## v0.0.6 *December 23, 2020* diff --git a/relayer-cli/src/commands/query.rs b/relayer-cli/src/commands/query.rs index e6147b4ba7..5888eb9da5 100644 --- a/relayer-cli/src/commands/query.rs +++ b/relayer-cli/src/commands/query.rs @@ -47,6 +47,10 @@ pub enum QueryConnectionCmds { /// The `query connection end` subcommand #[options(help = "query connection end")] End(connection::QueryConnectionEndCmd), + + /// The `query connection channels` subcommand + #[options(help = "query connection channels")] + Channels(connection::QueryConnectionChannelsCmd), } #[derive(Command, Debug, Options, Runnable)] diff --git a/relayer-cli/src/commands/query/connection.rs b/relayer-cli/src/commands/query/connection.rs index 9ab0a392c3..87cafd65ce 100644 --- a/relayer-cli/src/commands/query/connection.rs +++ b/relayer-cli/src/commands/query/connection.rs @@ -7,7 +7,7 @@ use ibc::ics03_connection::connection::ConnectionEnd; use ibc::ics24_host::error::ValidationError; use ibc::ics24_host::identifier::ChainId; use ibc::ics24_host::identifier::ConnectionId; - +use ibc_proto::ibc::core::channel::v1::QueryConnectionChannelsRequest; use relayer::chain::{Chain, CosmosSDKChain}; use relayer::config::{ChainConfig, Config}; @@ -96,11 +96,87 @@ impl Runnable for QueryConnectionEndCmd { } } +/// Command for querying the channel identifiers associated with a connection. +/// Sample invocation: +/// `cargo run --bin relayer -- -c simple_config.toml query connection channels ibc-0 connection-0` +#[derive(Clone, Command, Debug, Options)] +pub struct QueryConnectionChannelsCmd { + #[options(free, help = "identifier of the chain to query")] + chain_id: Option, + + #[options(free, help = "identifier of the connection to query")] + connection_id: Option, +} + +#[derive(Debug)] +struct QueryConnectionChannelsOptions { + connection_id: ConnectionId, +} + +impl QueryConnectionChannelsCmd { + fn validate_options( + &self, + config: &Config, + ) -> Result<(ChainConfig, QueryConnectionChannelsOptions), String> { + let chain_id = self + .chain_id + .clone() + .ok_or_else(|| "no chain chain identifier provided".to_string())?; + let chain_config = config + .find_chain(&chain_id) + .ok_or_else(|| "missing chain configuration for the given chain id".to_string())?; + + let connection_id = self + .connection_id + .as_ref() + .ok_or_else(|| "no connection identifier was provided".to_string())? + .parse() + .map_err(|err: ValidationError| err.to_string())?; + + let opts = QueryConnectionChannelsOptions { connection_id }; + + Ok((chain_config.clone(), opts)) + } +} + +impl Runnable for QueryConnectionChannelsCmd { + 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 rt = Arc::new(Mutex::new(TokioRuntime::new().unwrap())); + let chain = CosmosSDKChain::bootstrap(chain_config, rt).unwrap(); + + let req = QueryConnectionChannelsRequest { + connection: opts.connection_id.to_string(), + pagination: None, + }; + + let res: Result<_, Error> = chain + .query_connection_channels(req) + .map_err(|e| Kind::Query.context(e).into()); + + match res { + Ok(cs) => status_info!("connection channels query result: ", "{:?}", cs), + Err(e) => status_info!("connection channels query error", "{}", e), + } + } +} + #[cfg(test)] mod tests { - use crate::commands::query::connection::QueryConnectionEndCmd; use relayer::config::parse; + use crate::commands::query::connection::{QueryConnectionChannelsCmd, QueryConnectionEndCmd}; + #[test] fn parse_connection_query_end_parameters() { let default_params = QueryConnectionEndCmd { @@ -194,4 +270,96 @@ mod tests { } } } + + #[test] + fn parse_query_connection_channels_parameters() { + let default_params = QueryConnectionChannelsCmd { + chain_id: Some("ibc-0".to_string().parse().unwrap()), + connection_id: Some("ibconeconnection".to_string().parse().unwrap()), + }; + + struct Test { + name: String, + params: QueryConnectionChannelsCmd, + 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: QueryConnectionChannelsCmd { + chain_id: None, + ..default_params.clone() + }, + want_pass: false, + }, + Test { + name: "Chain not configured".to_string(), + params: QueryConnectionChannelsCmd { + chain_id: Some("ibc007".to_string().parse().unwrap()), + ..default_params.clone() + }, + want_pass: false, + }, + Test { + name: "No connection id specified".to_string(), + params: QueryConnectionChannelsCmd { + connection_id: None, + ..default_params.clone() + }, + want_pass: false, + }, + Test { + name: "Bad connection, non-alpha".to_string(), + params: QueryConnectionChannelsCmd { + connection_id: Some("connection-0^".to_string()), + ..default_params.clone() + }, + want_pass: false, + }, + Test { + name: "Bad connection, name too short".to_string(), + params: QueryConnectionChannelsCmd { + connection_id: Some("connshort".to_string()), + ..default_params + }, + 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 + ); + } + } + } + } } diff --git a/relayer/src/chain.rs b/relayer/src/chain.rs index 592ccf3dc2..1f320f6cdd 100644 --- a/relayer/src/chain.rs +++ b/relayer/src/chain.rs @@ -23,8 +23,8 @@ use tendermint::account::Id as AccountId; use tendermint::block::Height; use ibc_proto::ibc::core::channel::v1::{ - PacketState, QueryPacketAcknowledgementsRequest, QueryPacketCommitmentsRequest, - QueryUnreceivedAcksRequest, QueryUnreceivedPacketsRequest, + PacketState, QueryConnectionChannelsRequest, QueryPacketAcknowledgementsRequest, + QueryPacketCommitmentsRequest, QueryUnreceivedAcksRequest, QueryUnreceivedPacketsRequest, }; use ibc_proto::ibc::core::commitment::v1::MerkleProof; @@ -337,6 +337,12 @@ pub trait Chain: Sized { request: QueryUnreceivedAcksRequest, ) -> Result, Error>; + /// Performs a query to retrieve the identifiers of all channels associated with a connection. + fn query_connection_channels( + &self, + request: QueryConnectionChannelsRequest, + ) -> Result, Error>; + fn build_packet_proofs( &self, packet_type: PacketMsgType, diff --git a/relayer/src/chain/cosmos.rs b/relayer/src/chain/cosmos.rs index aef18dc56f..54bf2844ca 100644 --- a/relayer/src/chain/cosmos.rs +++ b/relayer/src/chain/cosmos.rs @@ -36,8 +36,8 @@ use ibc_proto::cosmos::tx::v1beta1::{AuthInfo, Fee, ModeInfo, SignDoc, SignerInf // Support for GRPC use ibc_proto::cosmos::auth::v1beta1::{BaseAccount, QueryAccountRequest}; use ibc_proto::ibc::core::channel::v1::{ - PacketState, QueryPacketAcknowledgementsRequest, QueryPacketCommitmentsRequest, - QueryUnreceivedAcksRequest, QueryUnreceivedPacketsRequest, + PacketState, QueryConnectionChannelsRequest, QueryPacketAcknowledgementsRequest, + QueryPacketCommitmentsRequest, QueryUnreceivedAcksRequest, QueryUnreceivedPacketsRequest, }; use ibc_proto::ibc::core::commitment::v1::MerkleProof; @@ -51,7 +51,7 @@ use ibc::ics07_tendermint::header::Header as TMHeader; use ibc::ics23_commitment::commitment::CommitmentPrefix; use ibc::ics23_commitment::merkle::convert_tm_to_ics_merkle_proof; -use ibc::ics24_host::identifier::{ChainId, ClientId}; +use ibc::ics24_host::identifier::{ChainId, ChannelId, ClientId}; use ibc::ics24_host::Path::ClientConsensusState as ClientConsensusPath; use ibc::ics24_host::Path::ClientState as ClientStatePath; use ibc::ics24_host::{Path, IBC_QUERY_PATH}; @@ -516,7 +516,6 @@ impl Chain for CosmosSDKChain { Ok(key) } /// Queries the packet commitment hashes associated with a channel. - /// TODO - move to the chain trait fn query_packet_commitments( &self, request: QueryPacketCommitmentsRequest, @@ -548,7 +547,6 @@ impl Chain for CosmosSDKChain { } /// Queries the packet commitment hashes associated with a channel. - /// TODO - move the chain trait fn query_unreceived_packets( &self, request: QueryUnreceivedPacketsRequest, @@ -572,7 +570,6 @@ impl Chain for CosmosSDKChain { } /// Queries the packet acknowledgment hashes associated with a channel. - /// TODO - move to the chain trait fn query_packet_acknowledgements( &self, request: QueryPacketAcknowledgementsRequest, @@ -604,7 +601,6 @@ impl Chain for CosmosSDKChain { } /// Queries the packet commitment hashes associated with a channel. - /// TODO - move the chain trait fn query_unreceived_acknowledgements( &self, request: QueryUnreceivedAcksRequest, @@ -655,6 +651,37 @@ impl Chain for CosmosSDKChain { } Ok(result) } + + fn query_connection_channels( + &self, + request: QueryConnectionChannelsRequest, + ) -> Result, Error> { + let grpc_addr = + Uri::from_str(&self.config().grpc_addr).map_err(|e| Kind::Grpc.context(e))?; + let mut client = self + .block_on( + ibc_proto::ibc::core::channel::v1::query_client::QueryClient::connect(grpc_addr), + )? + .map_err(|e| Kind::Grpc.context(e))?; + + let request = tonic::Request::new(request); + + let response = self + .block_on(client.connection_channels(request))? + .map_err(|e| Kind::Grpc.context(e))? + .into_inner(); + + // TODO: add warnings for any identifiers that fail to parse (below). + // https://github.com/informalsystems/ibc-rs/pull/506#discussion_r555945560 + + let vec_ids = response + .channels + .iter() + .filter_map(|ic| ChannelId::from_str(ic.channel_id.as_str()).ok()) + .collect(); + + Ok(vec_ids) + } } fn packet_query(request: &QueryPacketEventDataRequest, seq: &Sequence) -> Result { diff --git a/relayer/src/chain/mock.rs b/relayer/src/chain/mock.rs index 00bb753feb..9b216f9e9d 100644 --- a/relayer/src/chain/mock.rs +++ b/relayer/src/chain/mock.rs @@ -11,8 +11,8 @@ use tendermint::account::Id; use tendermint_testgen::light_block::TMLightBlock; use ibc_proto::ibc::core::channel::v1::{ - PacketState, QueryPacketAcknowledgementsRequest, QueryPacketCommitmentsRequest, - QueryUnreceivedAcksRequest, QueryUnreceivedPacketsRequest, + PacketState, QueryConnectionChannelsRequest, QueryPacketAcknowledgementsRequest, + QueryPacketCommitmentsRequest, QueryUnreceivedAcksRequest, QueryUnreceivedPacketsRequest, }; use ibc_proto::ibc::core::commitment::v1::MerkleProof; @@ -25,7 +25,7 @@ use ibc::ics07_tendermint::consensus_state::ConsensusState as TendermintConsensu use ibc::ics07_tendermint::header::Header as TendermintHeader; use ibc::ics18_relayer::context::ICS18Context; use ibc::ics23_commitment::commitment::CommitmentPrefix; -use ibc::ics24_host::identifier::{ChainId, ClientId}; +use ibc::ics24_host::identifier::{ChainId, ChannelId, ClientId}; use ibc::ics24_host::Path; use ibc::mock::context::MockContext; use ibc::mock::host::HostType; @@ -228,6 +228,13 @@ impl Chain for MockChain { unimplemented!() } + fn query_connection_channels( + &self, + _request: QueryConnectionChannelsRequest, + ) -> Result, Error> { + unimplemented!() + } + fn query_txs(&self, _request: QueryPacketEventDataRequest) -> Result, Error> { unimplemented!() }