Skip to content

Commit

Permalink
Relayer CLIs for client messages (informalsystems#251)
Browse files Browse the repository at this point in the history
* relayer create client cli

* Add conversion between MsgCreateClient and MsgCreateAnyClient

* added forgotten file

* added forgotten file

* Move functionality in create_clien() to be used by the relayer

* Remove ics07 messages

* Cleanup

* Move create_clien() in the relayer

* Review comments

* Review comments

* Add new get_dummy_.. helper function

* Remove backtrace from the output

* Review comment
  • Loading branch information
ancazamfir committed Sep 25, 2020
1 parent 4224310 commit f1e5103
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 2 deletions.
2 changes: 2 additions & 0 deletions relayer-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ tokio = { version = "0.2.13", features = ["rt-util", "sync"] }
tracing = "0.1.13"
tracing-subscriber = "0.2.3"
futures = "0.3.5"
prost = "0.6.1"
prost-types = { version = "0.6.1" }

[dependencies.abscissa_core]
version = "0.5.2"
Expand Down
6 changes: 6 additions & 0 deletions relayer-cli/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod light;
mod listen;
mod query;
mod start;
mod tx;
mod utils;
mod version;

Expand All @@ -18,6 +19,7 @@ use self::{
version::VersionCmd,
};

use crate::commands::tx::TxCmd;
use crate::config::Config;
use abscissa_core::{Command, Configurable, FrameworkError, Help, Options, Runnable};
use std::path::PathBuf;
Expand Down Expand Up @@ -52,6 +54,10 @@ pub enum CliCmd {
#[options(help = "query state from chain")]
Query(QueryCmd),

/// The `tx` subcommand
#[options(help = "create IBC transactions on configured chains")]
Tx(TxCmd),

/// The `light` subcommand
#[options(help = "basic functionality for managing the lite clients")]
Light(LightCmd),
Expand Down
20 changes: 20 additions & 0 deletions relayer-cli/src/commands/tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! `tx` subcommand
use crate::commands::tx::client::TxCreateClientCmd;
use abscissa_core::{Command, Options, Runnable};

mod client;

/// `tx` subcommand
#[derive(Command, Debug, Options, Runnable)]
pub enum TxCmd {
/// The `tx raw` subcommand submits IBC transactions to a chain
#[options(help = "tx raw")]
Raw(TxRawCommands),
}

#[derive(Command, Debug, Options, Runnable)]
pub enum TxRawCommands {
/// The `tx raw client-create` subcommand submits a MsgCreateClient in a transaction to a chain
#[options(help = "tx raw create-client")]
CreateClient(TxCreateClientCmd),
}
83 changes: 83 additions & 0 deletions relayer-cli/src/commands/tx/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use abscissa_core::{Command, Options, Runnable};
use relayer::config::Config;
use relayer::tx::client::{create_client, CreateClientOptions};

use crate::application::app_config;
use crate::error::{Error, Kind};
use crate::prelude::*;

#[derive(Clone, Command, Debug, Options)]
pub struct TxCreateClientCmd {
#[options(free, help = "identifier of the destination chain")]
dest_chain_id: Option<String>,

#[options(free, help = "identifier of the source chain")]
src_chain_id: Option<String>,

#[options(
free,
help = "identifier of the client to be created on destination chain"
)]
dest_client_id: Option<String>,
}

impl TxCreateClientCmd {
fn validate_options(&self, config: &Config) -> Result<CreateClientOptions, String> {
let dest_chain_id = self
.dest_chain_id
.clone()
.ok_or_else(|| "missing destination chain identifier".to_string())?;

let dest_chain_config = config
.chains
.iter()
.find(|c| c.id == dest_chain_id.parse().unwrap())
.ok_or_else(|| "missing destination chain configuration".to_string())?;

let src_chain_id = self
.src_chain_id
.clone()
.ok_or_else(|| "missing source chain identifier".to_string())?;

let src_chain_config = config
.chains
.iter()
.find(|c| c.id == src_chain_id.parse().unwrap())
.ok_or_else(|| "missing source chain configuration".to_string())?;

let dest_client_id = self
.dest_client_id
.as_ref()
.ok_or_else(|| "missing destination client identifier".to_string())?
.parse()
.map_err(|_| "bad client identifier".to_string())?;

Ok(CreateClientOptions {
dest_client_id,
dest_chain_config: dest_chain_config.clone(),
src_chain_config: src_chain_config.clone(),
})
}
}

impl Runnable for TxCreateClientCmd {
fn run(&self) {
let config = app_config();

let opts = match self.validate_options(&config) {
Err(err) => {
status_err!("invalid options: {}", err);
return;
}
Ok(result) => result,
};
status_info!("Message", "{:?}", opts);

let res: Result<(), Error> = create_client(opts).map_err(|e| Kind::Tx.context(e).into());

match res {
Ok(receipt) => status_info!("client created, result: ", "{:?}", receipt),
Err(e) => status_info!("client create failed, error: ", "{}", e),
}
}
}
4 changes: 4 additions & 0 deletions relayer-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub enum Kind {
/// Error during network query
#[error("query error")]
Query,

/// Error during transaction submission
#[error("tx error")]
Tx,
}

impl Kind {
Expand Down
1 change: 1 addition & 0 deletions relayer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ tokio = "0.2"
serde_json = { version = "1" }
bytes = "0.5.6"
prost = "0.6.1"
prost-types = { version = "0.6.1" }

[dev-dependencies]
21 changes: 20 additions & 1 deletion relayer/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use crate::config::ChainConfig;
use crate::error;
use std::error::Error;

mod cosmos;
pub(crate) mod cosmos;
pub use cosmos::CosmosSDKChain;
use prost_types::Any;

/// Handy type alias for the type of validator set associated with a chain
pub type ValidatorSet<Chain> = <<Chain as self::Chain>::Commit as tmlite::Commit>::ValidatorSet;
Expand Down Expand Up @@ -44,6 +45,9 @@ pub trait Chain {
/// Perform a generic `query`, and return the corresponding response data.
fn query(&self, data: Path, height: u64, prove: bool) -> Result<Vec<u8>, Self::Error>;

/// send a transaction with `msgs` to chain.
fn send(&self, _msgs: &[Any]) -> Result<(), Self::Error>;

/// Returns the chain's identifier
fn id(&self) -> &ChainId {
&self.config().id
Expand All @@ -61,6 +65,10 @@ pub trait Chain {
/// The trusting period configured for this chain
fn trusting_period(&self) -> Duration;

/// The unbonding period of this chain
/// TODO - this is a GRPC query, needs to be implemented
fn unbonding_period(&self) -> Duration;

/// The trust threshold configured for this chain
fn trust_threshold(&self) -> TrustThresholdFraction;
}
Expand All @@ -85,6 +93,17 @@ pub async fn query_latest_height(chain: &impl Chain) -> Result<Height, error::Er
Ok(status.sync_info.latest_block_height.into())
}

/// Query the latest header
pub async fn query_latest_header<C>(
chain: &C,
) -> Result<lite::SignedHeader<C::Commit, C::Header>, error::Error>
where
C: Chain,
{
let h = query_latest_height(chain).await?;
Ok(query_header_at_height::<C>(chain, h).await?)
}

/// Query a header at the given height via the RPC requester
pub async fn query_header_at_height<C>(
chain: &C,
Expand Down
14 changes: 13 additions & 1 deletion relayer/src/chain/cosmos.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use prost_types::Any;
use std::str::FromStr;
use std::time::Duration;

use tendermint::abci::Path as TendermintABCIPath;
Expand All @@ -17,7 +19,6 @@ use crate::config::ChainConfig;
use crate::error::{Error, Kind};

use super::Chain;
use std::str::FromStr;

pub struct CosmosSDKChain {
config: ChainConfig,
Expand Down Expand Up @@ -64,6 +65,12 @@ impl Chain for CosmosSDKChain {
Ok(response)
}

/// Send a transaction that includes the specified messages
fn send(&self, _msgs: &[Any]) -> Result<(), Error> {
// TODO sign and broadcast_tx
Ok(())
}

fn config(&self) -> &ChainConfig {
&self.config
}
Expand All @@ -80,6 +87,11 @@ impl Chain for CosmosSDKChain {
self.config.trusting_period
}

fn unbonding_period(&self) -> Duration {
// TODO - query chain
Duration::from_secs(24 * 7 * 3)
}

fn trust_threshold(&self) -> TrustThresholdFraction {
TrustThresholdFraction::default()
}
Expand Down
5 changes: 5 additions & 0 deletions relayer/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This module defines the various errors that be raised in the relayer.

use anomaly::{BoxError, Context};
use ibc::ics24_host::identifier::ClientId;
use thiserror::Error;

/// An error that can be raised by the relayer.
Expand Down Expand Up @@ -40,6 +41,10 @@ pub enum Kind {
/// Response does not contain data
#[error("Empty response value")]
EmptyResponseValue,

/// Create client failure
#[error("Failed to create client {0}: {1}")]
CreateClient(ClientId, String),
}

impl Kind {
Expand Down
1 change: 1 addition & 0 deletions relayer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ pub mod error;
pub mod event_handler;
pub mod event_monitor;
pub mod store;
pub mod tx;
pub mod util;
1 change: 1 addition & 0 deletions relayer/src/tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod client;
95 changes: 95 additions & 0 deletions relayer/src/tx/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use prost_types::Any;
use std::time::Duration;

use ibc::ics02_client::client_def::{AnyClientState, AnyConsensusState};
use ibc::ics02_client::client_type::ClientType;
use ibc::ics02_client::msgs::MsgCreateAnyClient;
use ibc::ics24_host::identifier::ClientId;
use ibc::ics24_host::Path::ClientState as ClientStatePath;
use ibc::tx_msg::Msg;

use tendermint::block::Height;

use crate::chain::cosmos::block_on;
use crate::chain::{query_latest_header, Chain, CosmosSDKChain};
use crate::config::ChainConfig;
use crate::error::{Error, Kind};

#[derive(Clone, Debug)]
pub struct CreateClientOptions {
pub dest_client_id: ClientId,
pub dest_chain_config: ChainConfig,
pub src_chain_config: ChainConfig,
}

pub fn create_client(opts: CreateClientOptions) -> Result<(), Error> {
// Ger the destination
let dest_chain = CosmosSDKChain::from_config(opts.clone().dest_chain_config)?;

// Query the client state on destination chain.
if dest_chain
.query(ClientStatePath(opts.clone().dest_client_id), 0, false)
.is_ok()
{
return Err(Into::<Error>::into(Kind::CreateClient(
opts.dest_client_id,
"client already exists".into(),
)));
}

// Get the latest header from the source chain and build the consensus state.
let src_chain = CosmosSDKChain::from_config(opts.clone().src_chain_config)?;
let tm_consensus_state = block_on(query_latest_header::<CosmosSDKChain>(&src_chain))
.map_err(|e| {
Kind::CreateClient(
opts.dest_client_id.clone(),
"failed to get the latest header".into(),
)
.context(e)
})
.map(ibc::ics07_tendermint::consensus_state::ConsensusState::from)?;

let any_consensus_state = AnyConsensusState::Tendermint(tm_consensus_state.clone());

// Build the client state.
let any_client_state = ibc::ics07_tendermint::client_state::ClientState::new(
src_chain.id().to_string(),
src_chain.trusting_period(),
src_chain.unbonding_period(),
Duration::from_millis(3000),
tm_consensus_state.height,
Height(0),
)
.map_err(|e| {
Kind::CreateClient(
opts.dest_client_id.clone(),
"failed to build the client state".into(),
)
.context(e)
})
.map(AnyClientState::Tendermint)?;

let signer = dest_chain.config().account_prefix.parse().map_err(|e| {
Kind::CreateClient(opts.dest_client_id.clone(), "bad signer".into()).context(e)
})?;

// Build the domain type message
let new_msg = MsgCreateAnyClient::new(
opts.dest_client_id,
ClientType::Tendermint,
any_client_state,
any_consensus_state,
signer,
);

// Create a proto any message
let mut proto_msgs: Vec<Any> = Vec::new();
let any_msg = Any {
// TODO - add get_url_type() to prepend proper string to get_type()
type_url: "/ibc.client.MsgCreateClient".to_ascii_lowercase(),
value: new_msg.get_sign_bytes(),
};

proto_msgs.push(any_msg);
dest_chain.send(&proto_msgs)
}

0 comments on commit f1e5103

Please sign in to comment.