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

Add support for multiple connection hops in create channel command #4067

Merged
merged 4 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
345 changes: 332 additions & 13 deletions crates/relayer-cli/src/commands/create/channel.rs

Large diffs are not rendered by default.

15 changes: 13 additions & 2 deletions crates/relayer-cli/src/commands/tx/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ pub struct TxChanOpenInitCmd {
)]
order: Ordering,

// --connection-hops receives a list of ConnectionId of intermediate connections between two chains
// if they are to be connected via a multihop channel. The list of connection identifiers passed to
// `--connection-hops` starts with the identifier of the connection that comes after `--dst-connection`
// in the channel path from `--dst-chain` towards `--src-chain`. For example, given the following
// channel path, where `--src-chain` is Chain-A and `--dst-chain` is Chain-D:
//
// +---------+ connection-3 +---------+ connection-2 +---------+ connection-1 +---------+
// | Chain-A | <---------------- | Chain-B | <---------------- | Chain-C | <---------------- | Chain-D |
// +---------+ +---------+ +---------+ +---------+
//
// The --connection-hops parameter should receive 'connection-2/connection-3' as argument.
#[clap(
long = "connection-hops",
value_name = "CONNECTION_HOPS",
Expand Down Expand Up @@ -182,9 +193,9 @@ impl Runnable for TxChanOpenInitCmd {
// possible ramifications.

// Check if connection IDs were provided via --connection-hops, indicating a multi-hop channel
if let Some(connnection_ids) = &self.conn_hop_ids {
if let Some(connection_ids) = &self.conn_hop_ids {
// Retrieve information for each of the remaining hops until the other end of the channel is reached
for connection_id in connnection_ids.as_slice().iter() {
for connection_id in connection_ids.as_slice().iter() {
// Retrieve the ChainId of the chain to which the last hop pointed to
let chain_id = &b_side_hops
.last()
Expand Down
11 changes: 10 additions & 1 deletion crates/relayer-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use tendermint::Error as TendermintError;

use ibc_relayer_types::applications::ics29_fee::error::Error as FeeError;
use ibc_relayer_types::core::ics04_channel::channel::IdentifiedChannelEnd;
use ibc_relayer_types::core::ics24_host::identifier::ChainId;
use ibc_relayer_types::core::ics24_host::identifier::{ChainId, ConnectionIds};
use ibc_relayer_types::signer::SignerError;

use ibc_relayer::channel::ChannelError;
Expand Down Expand Up @@ -131,5 +131,14 @@ define_error! {
but the received connection identifier(s) lead to chain '{}' ", e.dst_chain, e.src_chain,
e.reference_chain)
},

Ics33HopsReturnToSource
{
channel_path: ConnectionIds,
src_chain: ChainId,
}
| e | {
format_args!("the connection hops '{}' form a channel path that starts and ends on the same chain ('{}')", e.channel_path, e.src_chain)
},
}
}
46 changes: 44 additions & 2 deletions crates/relayer/src/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub use error::ChannelError;
use ibc_proto::google::protobuf::Any;
use ibc_proto::ibc::core::channel::v1::{MsgMultihopProofs, MultihopProof};
use ibc_proto::Protobuf;
use ibc_relayer_types::core::ics03_connection::connection::IdentifiedConnectionEnd;
use ibc_relayer_types::core::ics04_channel::channel::{
ChannelEnd, Counterparty, IdentifiedChannelEnd, Ordering, State,
};
Expand Down Expand Up @@ -225,7 +226,7 @@ impl<ChainA: ChainHandle, ChainB: ChainHandle> Channel<ChainA, ChainB> {
a_side: ChannelSide::new(
connection.src_chain(),
connection.src_client_id().clone(),
src_connection_id.clone(),
src_connection_id.clone(), // FIXME: We may want to remove this in favor of using only a_side_hops
a_side_hops,
a_port,
Default::default(),
Expand All @@ -234,7 +235,7 @@ impl<ChainA: ChainHandle, ChainB: ChainHandle> Channel<ChainA, ChainB> {
b_side: ChannelSide::new(
connection.dst_chain(),
connection.dst_client_id().clone(),
dst_connection_id.clone(),
dst_connection_id.clone(), // FIXME: We may want to remove this in favor of using only b_side_hops
b_side_hops,
b_port,
Default::default(),
Expand All @@ -248,6 +249,47 @@ impl<ChainA: ChainHandle, ChainB: ChainHandle> Channel<ChainA, ChainB> {
Ok(channel)
}

pub fn new_multihop(
a_chain: ChainA,
b_chain: ChainB,
a_side_connection: IdentifiedConnectionEnd,
b_side_connection: IdentifiedConnectionEnd,
ordering: Ordering,
a_port: PortId,
b_port: PortId,
a_side_hops: Option<ConnectionHops>,
b_side_hops: Option<ConnectionHops>,
version: Option<Version>,
connection_delay: Duration,
) -> Result<Self, ChannelError> {
let mut channel = Self {
ordering,
a_side: ChannelSide::new(
a_chain,
a_side_connection.end().client_id().clone(),
a_side_connection.id().clone(), // FIXME: We may want to remove this in favor of using only a_side_hops
a_side_hops,
a_port,
Default::default(),
version.clone(),
),
b_side: ChannelSide::new(
b_chain,
b_side_connection.end().client_id().clone(),
b_side_connection.id().clone(), // FIXME: We may want to remove this in favor of using only b_side_hops
b_side_hops,
b_port,
Default::default(),
version,
),
connection_delay,
};

channel.handshake()?;

Ok(channel)
}

pub fn restore_from_event(
chain: ChainA,
counterparty_chain: ChainB,
Expand Down
1 change: 1 addition & 0 deletions guide/src/templates/commands/hermes/create/channel_3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[[#BINARY hermes]][[#GLOBALOPTIONS]] create channel[[#OPTIONS]] --a-chain [[#A_CHAIN_ID]] --a-connection [[#A_CONNECTION_ID]] --connection-hops [[#CONNECTION_HOP_IDS]] --a-port [[#A_PORT_ID]] --b-port [[#B_PORT_ID]]
1 change: 1 addition & 0 deletions guide/src/templates/commands/hermes/create/channel_4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NOTE: The `--new-client-connection` option does not support connection hops. To open a multi-hop channel, please provide existing connections or initialize them manually before invoking this command.
10 changes: 10 additions & 0 deletions guide/src/templates/help_templates/create/channel.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ USAGE:

hermes create channel [OPTIONS] --a-chain <A_CHAIN_ID> --b-chain <B_CHAIN_ID> --a-port <A_PORT_ID> --b-port <B_PORT_ID> --new-client-connection

hermes create channel [OPTIONS] --a-chain <A_CHAIN_ID> --a-connection <A_CONNECTION_ID> --connection-hops <CONNECTION_HOP_IDS> --a-port <A_PORT_ID> --b-port <B_PORT_ID>

NOTE: The `--new-client-connection` option does not support connection hops. To open a multi-hop channel, please provide existing connections or initialize them manually before invoking this command.

OPTIONS:
--channel-version <VERSION>
The version for the new channel
Expand Down Expand Up @@ -48,3 +52,9 @@ FLAGS:

--b-port <B_PORT_ID>
Identifier of the side `b` port for the new channel

--connection-hops <CONNECTION_IDS>
A list of identifiers of the intermediate connections between side `a` and side `b` for
a multi-hop channel, separated by slashes, e.g, 'connection-1/connection-0' (optional).

[aliases: conn-hops]
Loading