use eyre::{eyre, Report as Error};
use ibc_relayer::chain::handle::ChainHandle;
use ibc_relayer::channel::{Channel, ChannelSide};
use ibc_relayer_types::core::ics04_channel::channel::Ordering;
use ibc_relayer_types::core::ics04_channel::version::Version;
use ibc_relayer_types::core::ics24_host::identifier::PortId;
use tracing::{debug, info};
use super::connection::{bootstrap_connection, BootstrapConnectionOptions};
use crate::types::binary::chains::ConnectedChains;
use crate::types::binary::channel::ConnectedChannel;
use crate::types::binary::connection::ConnectedConnection;
use crate::types::binary::foreign_client::ForeignClientPair;
use crate::types::id::TaggedPortIdRef;
use crate::types::tagged::*;
use crate::util::random::random_u64_range;
pub struct BootstrapChannelOptions {
pub order: Ordering,
pub version: Version,
pub pad_channel_id_a: u64,
pub pad_channel_id_b: u64,
}
pub fn bootstrap_channel_with_chains<ChainA: ChainHandle, ChainB: ChainHandle>(
chains: &ConnectedChains<ChainA, ChainB>,
port_a: &PortId,
port_b: &PortId,
connection_options: BootstrapConnectionOptions,
channel_options: BootstrapChannelOptions,
) -> Result<ConnectedChannel<ChainA, ChainB>, Error> {
let channel = bootstrap_channel(
&chains.foreign_clients,
&DualTagged::new(port_a),
&DualTagged::new(port_b),
connection_options,
channel_options,
)?;
Ok(channel)
}
pub fn bootstrap_channel<ChainA: ChainHandle, ChainB: ChainHandle>(
foreign_clients: &ForeignClientPair<ChainA, ChainB>,
port_a: &TaggedPortIdRef<ChainA, ChainB>,
port_b: &TaggedPortIdRef<ChainB, ChainA>,
connection_options: BootstrapConnectionOptions,
channel_options: BootstrapChannelOptions,
) -> Result<ConnectedChannel<ChainA, ChainB>, Error> {
let connection = bootstrap_connection(foreign_clients, connection_options)?;
bootstrap_channel_with_connection(
&foreign_clients.handle_a(),
&foreign_clients.handle_b(),
connection,
port_a,
port_b,
channel_options,
)
}
pub fn bootstrap_channel_with_connection<ChainA: ChainHandle, ChainB: ChainHandle>(
chain_a: &ChainA,
chain_b: &ChainB,
connection: ConnectedConnection<ChainA, ChainB>,
port_a: &TaggedPortIdRef<ChainA, ChainB>,
port_b: &TaggedPortIdRef<ChainB, ChainA>,
options: BootstrapChannelOptions,
) -> Result<ConnectedChannel<ChainA, ChainB>, Error> {
pad_channel_id(
chain_a,
chain_b,
&connection,
port_a,
options.pad_channel_id_a,
)?;
pad_channel_id(
chain_b,
chain_a,
&connection.clone().flip(),
port_b,
options.pad_channel_id_b,
)?;
let channel = Channel::new(
connection.connection.clone(),
options.order,
port_a.0.clone(),
port_b.0.clone(),
Some(options.version),
)?;
let channel_id_a = channel
.a_side
.channel_id()
.ok_or_else(|| eyre!("expect channel id"))?
.clone();
let channel_id_b = channel
.b_side
.channel_id()
.ok_or_else(|| eyre!("expect channel id"))?
.clone();
info!(
"created new chain/client/connection/channel from {}/{}/{}/{} to {}/{}/{}/{}",
chain_a.id(),
connection.client_ids.client_id_a,
connection.connection_id_a,
channel_id_a,
chain_b.id(),
connection.client_ids.client_id_b,
connection.connection_id_b,
channel_id_b,
);
let res = ConnectedChannel {
connection,
channel,
channel_id_a: DualTagged::new(channel_id_a),
channel_id_b: DualTagged::new(channel_id_b),
port_a: port_a.cloned(),
port_b: port_b.cloned(),
};
Ok(res)
}
pub fn pad_channel_id<ChainA: ChainHandle, ChainB: ChainHandle>(
chain_a: &ChainA,
chain_b: &ChainB,
connection: &ConnectedConnection<ChainA, ChainB>,
port_id: &TaggedPortIdRef<ChainA, ChainB>,
pad_count: u64,
) -> Result<(), Error> {
let client_id_a = &connection.client_ids.client_id_a;
let client_id_b = &connection.client_ids.client_id_b;
for i in 0..pad_count {
debug!(
"creating new channel id {} on chain/connection/client {}/{}/{}",
i + 1,
chain_a.id(),
connection.connection_id_a,
client_id_a,
);
let channel: Channel<ChainB, ChainA> = Channel {
ordering: Ordering::Unordered,
a_side: ChannelSide::new(
chain_b.clone(),
client_id_b.value().clone(),
connection.connection_id_b.value().clone(),
port_id.cloned().into_value(),
None,
None,
),
b_side: ChannelSide::new(
chain_a.clone(),
client_id_a.value().clone(),
connection.connection_id_a.value().clone(),
port_id.cloned().into_value(),
None,
None,
),
connection_delay: connection.connection.delay_period,
};
channel.build_chan_open_init_and_send()?;
}
Ok(())
}
impl Default for BootstrapChannelOptions {
fn default() -> Self {
Self {
order: Ordering::Unordered,
version: Version::ics20(),
pad_channel_id_a: 0,
pad_channel_id_b: 1,
}
}
}
impl BootstrapChannelOptions {
pub fn order(mut self, order: Ordering) -> Self {
self.order = order;
self
}
pub fn version(mut self, version: Version) -> Self {
self.version = version;
self
}
pub fn bootstrap_with_random_ids(mut self, bootstrap_with_random_ids: bool) -> Self {
if bootstrap_with_random_ids {
self.pad_channel_id_a = random_u64_range(0, 6);
self.pad_channel_id_b = random_u64_range(0, 6);
} else {
self.pad_channel_id_a = 0;
self.pad_channel_id_b = 1;
}
self
}
}