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

Implement query for client connections #169

Merged
merged 11 commits into from
Jul 29, 2020
24 changes: 24 additions & 0 deletions modules/src/ics02_client/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,34 @@ use std::marker::PhantomData;
use crate::ics23_commitment::{CommitmentPath, CommitmentProof};

//use crate::ics02_client::state::{ClientState, ConsensusState};
use crate::ics03_connection::error::Kind;
use crate::ics24_host::identifier::ClientId;
use crate::path::{ClientStatePath, ConsensusStatePath};
use crate::try_from_raw::TryFromRaw;
use crate::Height;

//TODO: This might need to be migrated to ibc-proto crate. But ClientConnections (as array of strings)
// might not be part of an official proto file
andynog marked this conversation as resolved.
Show resolved Hide resolved
#[derive(::prost::Message)]
pub struct RawClientConnections {
#[prost(string, repeated, tag = "1")]
pub connections: ::std::vec::Vec<String>,
}

impl TryFromRaw for Vec<String> {
type RawType = RawClientConnections;
type Error = anomaly::Error<Kind>;
fn try_from(value: RawClientConnections) -> Result<Self, Self::Error> {
let mut client_connections: Vec<String> = vec![];
for conn in value.connections {
client_connections.push(conn);
}
Ok(client_connections)
andynog marked this conversation as resolved.
Show resolved Hide resolved
// If no connections return error
andynog marked this conversation as resolved.
Show resolved Hide resolved
//Err(Kind::MissingCounterparty.into()),
}
}

pub struct QueryClientFullState<CLS> {
pub chain_height: Height,
pub client_id: ClientId,
Expand Down
16 changes: 16 additions & 0 deletions modules/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,22 @@ impl Path for ClientStatePath {
}
}

pub struct ClientConnectionsPath {
pub client_id: ClientId,
}

impl ClientConnectionsPath {
pub fn new(client_id: ClientId) -> Self {
Self { client_id }
}
}

impl Path for ClientConnectionsPath {
fn to_string(&self) -> String {
paths::client_connections_path(&self)
}
}

pub struct ChannelPath {
pub port_id: PortId,
channel_id: ChannelId,
Expand Down
5 changes: 5 additions & 0 deletions modules/src/path/cosmos.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use super::{ChannelPath, ClientStatePath, ConnectionPath, ConsensusStatePath};
use crate::path::ClientConnectionsPath;

pub fn connection_path(path: &ConnectionPath) -> String {
format!("connection/{}", path.connection_id)
Expand All @@ -12,6 +13,10 @@ pub fn client_state_path(path: &ClientStatePath) -> String {
format!("clientState/{}", path.client_id)
}

pub fn client_connections_path(path: &ClientConnectionsPath) -> String {
format!("clients/{}/connections", path.client_id)
}

pub fn channel_path(path: &ChannelPath) -> String {
format!("ports/{}/channels/{}", path.port_id, path.channel_id)
}
8 changes: 7 additions & 1 deletion modules/src/path/ics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::{ChannelPath, ClientStatePath, ConnectionPath, ConsensusStatePath};
use super::{
ChannelPath, ClientConnectionsPath, ClientStatePath, ConnectionPath, ConsensusStatePath,
};

pub fn consensus_state_path(path: &ConsensusStatePath) -> String {
format!("clients/{}/consensusState/{}", path.client_id, path.height)
Expand All @@ -8,6 +10,10 @@ pub fn client_state_path(path: &ClientStatePath) -> String {
format!("clients/{}/clientState", path.client_id)
}

pub fn client_connections_path(path: &ClientConnectionsPath) -> String {
format!("clients/{}/connections", path.client_id)
}

pub fn connection_path(path: &ConnectionPath) -> String {
format!("connections/{}", path.connection_id)
}
Expand Down
4 changes: 4 additions & 0 deletions relayer/cli/src/commands/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
192 changes: 190 additions & 2 deletions relayer/cli/src/commands/query/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,20 @@ use relayer::config::{ChainConfig, Config};
//use relayer_modules::ics02_client::query::QueryClientFullState;
use relayer_modules::ics24_host::identifier::ClientId;

//use crate::commands::utils::block_on;
use crate::commands::utils::block_on;
use relayer::chain::tendermint::TendermintChain;
andynog marked this conversation as resolved.
Show resolved Hide resolved
use relayer::query::{query, Request};
use relayer_modules::error::Error;
use relayer_modules::ics24_host::error::ValidationError;
use relayer_modules::path::{ClientConnectionsPath, Path};
use std::str::FromStr;
use tendermint::abci::Path as TendermintPath;
use tendermint::chain::Id as ChainId;

// *****************************************************************
andynog marked this conversation as resolved.
Show resolved Hide resolved
// Query client state
// *****************************************************************

#[derive(Clone, Command, Debug, Options)]
pub struct QueryClientStateCmd {
#[options(free, help = "identifier of the chain to query")]
Expand Down Expand Up @@ -95,6 +104,10 @@ impl Runnable for QueryClientStateCmd {
}
}

// *****************************************************************
// Query client consensus
// *****************************************************************

#[derive(Clone, Command, Debug, Options)]
pub struct QueryClientConsensusCmd {
#[options(free, help = "identifier of the chain to query")]
Expand Down Expand Up @@ -214,9 +227,98 @@ fn validate_common_options(
Ok((chain_config.clone(), client_id))
}

// *****************************************************************
// Query client connections
// *****************************************************************

#[derive(Clone, Command, Debug, Options)]
pub struct QueryClientConnectionsCmd {
#[options(free, help = "identifier of the chain to query")]
chain_id: Option<ChainId>,

#[options(free, help = "identifier of the client to query")]
client_id: Option<String>,

#[options(help = "height of the state to query", short = "h")]
height: Option<u64>,

#[options(help = "whether proof is required", short = "p")]
proof: Option<bool>,
}

#[derive(Debug)]
struct QueryClientConnectionsOptions {
client_id: ClientId,
height: u64,
proof: bool,
}

impl Into<Request> for QueryClientConnectionsOptions {
fn into(self) -> Request {
Request {
path: Some(TendermintPath::from_str(&"store/ibc/key").unwrap()),
data: ClientConnectionsPath::new(self.client_id).to_string(),
height: self.height,
prove: self.proof,
}
}
}

impl QueryClientConnectionsCmd {
fn validate_options(
&self,
config: &Config,
) -> Result<(ChainConfig, QueryClientConnectionsOptions), String> {
let (chain_config, client_id) =
validate_common_options(&self.chain_id, &self.client_id, config)?;

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, opts))
}
}

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);

// run without proof:
// cargo run --bin relayer -- -c relayer/relay/tests/config/fixtures/simple_config.toml query client connections ibc-test ethbridge --height 3 -p false
let chain = TendermintChain::from_config(chain_config).unwrap();
andynog marked this conversation as resolved.
Show resolved Hide resolved
let res: Result<Vec<String>, Error> = block_on(query(&chain, opts));

match res {
Ok(cs) => status_info!("query client connections result: ", "{:?}", cs),
Err(e) => status_info!("query client connections 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]
Expand Down Expand Up @@ -304,4 +406,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<Test> = 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
);
}
}
}
}
}