Skip to content

Commit

Permalink
Generic create channel CLI (#528)
Browse files Browse the repository at this point in the history
* Implement generic create channel cli

* Remove old create channel cli

* Wire up create channel cli

* Parse port IDs from optional strings

* Update deps and add PortId ArgParser implementation

* Remove some unnecessary trait bounds from PortId ArgParser

* Trying to fix return type

* Implement ParseInitCosmosChannelOptions

* Wire up ParseInitCosmosChannelOptions

* Pushing up changes

* Fix errors

* Remove unnecessary constraints

---------

Co-authored-by: Soares Chen <[email protected]>
  • Loading branch information
seanchen1991 and soareschen authored Jan 17, 2025
1 parent 4414c12 commit 3ff219f
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 150 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/cli/cli-components/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ hermes-relayer-components = { workspace = true }
hermes-encoding-components = { workspace = true }
hermes-logging-components = { workspace = true }
hermes-test-components = { workspace = true }
hermes-cosmos-chain-components = { workspace = true }

cgp = { workspace = true }
http = { workspace = true }
ibc = { workspace = true }
serde = { workspace = true, features = ["derive"] }
toml = { workspace = true }
clap = { workspace = true, features = ["derive"] }
176 changes: 176 additions & 0 deletions crates/cli/cli-components/src/impls/commands/channel/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use core::fmt::Display;
use core::marker::PhantomData;

use cgp::core::field::Index;
use cgp::prelude::*;
use hermes_logging_components::traits::has_logger::HasLogger;
use hermes_logging_components::traits::logger::CanLog;
use hermes_logging_components::types::level::LevelInfo;
use hermes_relayer_components::build::traits::builders::relay_builder::CanBuildRelay;
use hermes_relayer_components::chain::traits::types::chain_id::HasChainIdType;
use hermes_relayer_components::chain::traits::types::channel::HasInitChannelOptionsType;
use hermes_relayer_components::chain::traits::types::ibc::{HasClientIdType, HasIbcChainTypes};
use hermes_relayer_components::multi::traits::chain_at::HasChainTypeAt;
use hermes_relayer_components::multi::traits::relay_at::HasRelayTypeAt;
use hermes_relayer_components::relay::impls::channel::bootstrap::CanBootstrapChannel;
use hermes_relayer_components::relay::traits::chains::HasRelayChains;

use crate::traits::build::CanLoadBuilder;
use crate::traits::command::CommandRunner;
use crate::traits::output::{CanProduceOutput, HasOutputType};
use crate::traits::parse::CanParseArg;

pub struct RunCreateChannelCommand;

#[derive(Debug, clap::Parser, HasField)]
pub struct CreateChannelArgs {
#[clap(
long = "target-chain-id",
required = true,
value_name = "TARGET_CHAIN_ID",
help_heading = "REQUIRED"
)]
target_chain_id: String,

#[clap(
long = "target-client-id",
required = true,
value_name = "TARGET_CLIENT_ID",
help_heading = "REQUIRED"
)]
target_client_id: String,

#[clap(
long = "target-connection-id",
required = true,
value_name = "TARGET_CONNECTION_ID",
help_heading = "REQUIRED"
)]
target_connection_id: String,

#[clap(long = "target-port-id", value_name = "TARGET_PORT_ID")]
target_port_id: String,

#[clap(
long = "counterparty-chain-id",
required = true,
value_name = "COUNTERPARTY_CHAIN_ID",
help_heading = "REQUIRED"
)]
counterparty_chain_id: String,

#[clap(
long = "counterparty-client-id",
required = true,
value_name = "COUNTERPARTY_CLIENT_ID",
help_heading = "REQUIRED"
)]
counterparty_client_id: String,

#[clap(long = "counterparty-port-id", value_name = "COUNTERPARTY_PORT_ID")]
counterparty_port_id: String,

#[clap(long = "ordering", value_name = "ORDERING")]
ordering: String,

#[clap(long = "version", value_name = "VERSION")]
version: String,
}

impl<App, Args, Builder, Chain, Counterparty, Relay> CommandRunner<App, Args>
for RunCreateChannelCommand
where
App: CanLoadBuilder<Builder = Builder>
+ HasOutputType
+ HasErrorType
+ HasLogger
+ CanProduceOutput<&'static str>
+ CanRaiseError<Builder::Error>
+ CanRaiseError<Relay::Error>
+ CanParseArg<Args, symbol!("target_chain_id"), Parsed = Chain::ChainId>
+ CanParseArg<Args, symbol!("target_client_id"), Parsed = Chain::ClientId>
+ CanParseArg<Args, symbol!("target_port_id"), Parsed = Chain::PortId>
+ CanParseArg<Args, symbol!("counterparty_chain_id"), Parsed = Counterparty::ChainId>
+ CanParseArg<Args, symbol!("counterparty_client_id"), Parsed = Counterparty::ClientId>
+ CanParseArg<Args, symbol!("counterparty_port_id"), Parsed = Counterparty::PortId>
+ CanParseArg<Args, symbol!("init_channel_options"), Parsed = Chain::InitChannelOptions>,
App::Logger: CanLog<LevelInfo>,
Args: Async,
Builder: CanBuildRelay<Index<0>, Index<1>, Relay = Relay>
+ HasChainTypeAt<Index<0>, Chain = Chain>
+ HasChainTypeAt<Index<1>, Chain = Counterparty>
+ HasRelayTypeAt<Index<0>, Index<1>>,
Chain: HasChainIdType
+ HasErrorType
+ HasClientIdType<Counterparty>
+ HasInitChannelOptionsType<Counterparty>
+ HasIbcChainTypes<Counterparty>,
Chain::InitChannelOptions: Default,
Chain::ChainId: Display,
Chain::ClientId: Display,
Chain::ChannelId: Display,
Counterparty::ChainId: Display,
Counterparty::ClientId: Display,
Counterparty::ChannelId: Display,
Counterparty: HasChainIdType + HasClientIdType<Chain> + HasIbcChainTypes<Chain> + HasErrorType,
Relay: CanBootstrapChannel + HasRelayChains<SrcChain = Chain, DstChain = Counterparty>,
{
async fn run_command(app: &App, args: &Args) -> Result<App::Output, App::Error> {
let logger = app.logger();
let builder = app.load_builder().await?;

let target_chain_id = app.parse_arg(args, PhantomData::<symbol!("target_chain_id")>)?;
let target_client_id = app.parse_arg(args, PhantomData::<symbol!("target_client_id")>)?;
let target_port_id = app.parse_arg(args, PhantomData::<symbol!("target_port_id")>)?;
let counterparty_chain_id =
app.parse_arg(args, PhantomData::<symbol!("counterparty_chain_id")>)?;
let counterparty_client_id =
app.parse_arg(args, PhantomData::<symbol!("counterparty_client_id")>)?;
let counterparty_port_id =
app.parse_arg(args, PhantomData::<symbol!("counterparty_port_id")>)?;

let relay = builder
.build_relay(
PhantomData::<(Index<0>, Index<1>)>,
&target_chain_id,
&counterparty_chain_id,
&target_client_id,
&counterparty_client_id,
)
.await
.map_err(App::raise_error)?;

logger
.log(
&format!(
"Creating channel between {}:{} and {}:{} ...",
target_chain_id,
target_client_id,
counterparty_chain_id,
counterparty_client_id,
),
&LevelInfo,
)
.await;

let (target_channel_id, counterparty_channel_id) = relay
.bootstrap_channel(&target_port_id, &counterparty_port_id, &Default::default())
.await
.map_err(App::raise_error)?;

logger
.log(
&format!(
"Channel {}:{} successfully created between {} and {}",
target_channel_id,
counterparty_channel_id,
target_chain_id,
counterparty_chain_id,
),
&LevelInfo,
)
.await;

Ok(app.produce_output("Done"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod create;
1 change: 1 addition & 0 deletions crates/cli/cli-components/src/impls/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod bootstrap;
pub mod channel;
pub mod client;
pub mod connection;
pub mod queries;
Expand Down
79 changes: 79 additions & 0 deletions crates/cli/cli-components/src/impls/parse/identifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use core::marker::PhantomData;

use cgp::prelude::*;
use hermes_cosmos_chain_components::types::channel::CosmosInitChannelOptions;
use ibc::core::channel::types::channel::Order;
use ibc::core::channel::types::Version;
use ibc::core::host::types::error::IdentifierError;
use ibc::core::host::types::identifiers::{ConnectionId, PortId};

use crate::traits::parse::ArgParser;

const DEFAULT_VERSION: &str = "ics20-1";

pub struct ParsePortId;

impl<App, Args, Tag> ArgParser<App, Args, Tag> for ParsePortId
where
App: CanRaiseAsyncError<IdentifierError>,
Args: HasField<Tag, Value = String>,
{
type Parsed = PortId;

fn parse_arg(
_app: &App,
args: &Args,
_tag: PhantomData<Tag>,
) -> Result<Self::Parsed, App::Error> {
if let Ok(port_id) = args.get_field(PhantomData).parse::<PortId>() {
Ok(port_id)
} else {
Ok(PortId::transfer())
}
}
}

pub struct ParseInitCosmosChannelOptions;

impl<App, Args, Tag> ArgParser<App, Args, Tag> for ParseInitCosmosChannelOptions
where
App: HasAsyncErrorType,
Args: HasField<symbol!("target_connection_id"), Value = String>
+ HasField<symbol!("version"), Value = String>
+ HasField<symbol!("ordering"), Value = String>,
{
type Parsed = CosmosInitChannelOptions;

fn parse_arg(
_app: &App,
args: &Args,
_tag: PhantomData<Tag>,
) -> Result<Self::Parsed, App::Error> {
let connection_hops = if let Ok(conn_id) = args
.get_field(PhantomData::<symbol!("target_connection_id")>)
.parse::<ConnectionId>()
{
vec![conn_id]
} else {
Default::default()
};

let ordering =
if let Ok(ordering) = args.get_field(PhantomData::<symbol!("ordering")>).parse() {
ordering
} else {
Order::Unordered
};

let channel_version = match args.get_field(PhantomData::<symbol!("version")>).parse() {
Ok(version) => version,
Err(_) => Version::new(DEFAULT_VERSION.to_string()),
};

Ok(CosmosInitChannelOptions {
connection_hops,
ordering,
channel_version,
})
}
}
1 change: 1 addition & 0 deletions crates/cli/cli-components/src/impls/parse/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod field;
pub mod identifier;
pub mod string;
Loading

0 comments on commit 3ff219f

Please sign in to comment.