use abscissa_core::clap::Parser;
use abscissa_core::config::Override;
use abscissa_core::{Command, FrameworkErrorKind, Runnable};
use ibc_relayer::chain::handle::{BaseChainHandle, ChainHandle};
use ibc_relayer::config::Config;
use ibc_relayer::link::error::LinkError;
use ibc_relayer::link::{Link, LinkParameters};
use ibc_relayer_types::core::ics24_host::identifier::{ChainId, ChannelId, PortId};
use ibc_relayer_types::events::IbcEvent;
use crate::application::app_config;
use crate::cli_utils::spawn_chain_counterparty;
use crate::conclude::Output;
use crate::error::Error;
#[derive(Command, Debug, Parser, Runnable)]
pub enum ClearCmds {
Packets(ClearPacketsCmd),
}
#[derive(Debug, Parser, Command, PartialEq, Eq)]
pub struct ClearPacketsCmd {
#[clap(
long = "chain",
required = true,
value_name = "CHAIN_ID",
help_heading = "REQUIRED",
help = "Identifier of the chain"
)]
chain_id: ChainId,
#[clap(
long = "port",
required = true,
value_name = "PORT_ID",
help_heading = "REQUIRED",
help = "Identifier of the port"
)]
port_id: PortId,
#[clap(
long = "channel",
alias = "chan",
required = true,
value_name = "CHANNEL_ID",
help_heading = "REQUIRED",
help = "Identifier of the channel"
)]
channel_id: ChannelId,
#[clap(
long = "key-name",
help = "use the given signing key for the specified chain (default: `key_name` config)"
)]
key_name: Option<String>,
#[clap(
long = "counterparty-key-name",
help = "use the given signing key for the counterparty chain (default: `counterparty_key_name` config)"
)]
counterparty_key_name: Option<String>,
}
impl Override<Config> for ClearPacketsCmd {
fn override_config(&self, mut config: Config) -> Result<Config, abscissa_core::FrameworkError> {
let chain_config = config.find_chain_mut(&self.chain_id).ok_or_else(|| {
FrameworkErrorKind::ComponentError.context(format!(
"missing configuration for chain '{}'",
self.chain_id
))
})?;
if let Some(ref key_name) = self.key_name {
chain_config.key_name = key_name.to_string();
}
Ok(config)
}
}
impl Runnable for ClearPacketsCmd {
fn run(&self) {
let config = app_config();
let chains = match spawn_chain_counterparty::<BaseChainHandle>(
&config,
&self.chain_id,
&self.port_id,
&self.channel_id,
) {
Ok((chains, _)) => chains,
Err(e) => Output::error(format!("{}", e)).exit(),
};
if let Some(ref counterparty_key_name) = self.counterparty_key_name {
match chains.dst.config() {
Ok(mut dst_chain_cfg) => {
dst_chain_cfg.key_name = counterparty_key_name.to_string();
}
Err(e) => Output::error(format!("{}", e)).exit(),
}
}
let mut ev_list = vec![];
let opts = LinkParameters {
src_port_id: self.port_id.clone(),
src_channel_id: self.channel_id.clone(),
};
let fwd_link = match Link::new_from_opts(chains.src.clone(), chains.dst, opts, false, false)
{
Ok(link) => link,
Err(e) => Output::error(format!("{}", e)).exit(),
};
let rev_link = match fwd_link.reverse(false, false) {
Ok(link) => link,
Err(e) => Output::error(format!("{}", e)).exit(),
};
run_and_collect_events(&mut ev_list, || {
fwd_link.relay_recv_packet_and_timeout_messages()
});
run_and_collect_events(&mut ev_list, || {
rev_link.relay_recv_packet_and_timeout_messages()
});
run_and_collect_events(&mut ev_list, || fwd_link.relay_ack_packet_messages());
run_and_collect_events(&mut ev_list, || rev_link.relay_ack_packet_messages());
Output::success(ev_list).exit()
}
}
fn run_and_collect_events<F>(ev_list: &mut Vec<IbcEvent>, f: F)
where
F: FnOnce() -> Result<Vec<IbcEvent>, LinkError>,
{
match f() {
Ok(mut ev) => ev_list.append(&mut ev),
Err(e) => Output::error(Error::link(e)).exit(),
};
}
#[cfg(test)]
mod tests {
use super::ClearPacketsCmd;
use std::str::FromStr;
use abscissa_core::clap::Parser;
use ibc_relayer_types::core::ics24_host::identifier::{ChainId, ChannelId, PortId};
#[test]
fn test_clear_packets_required_only() {
assert_eq!(
ClearPacketsCmd {
chain_id: ChainId::from_string("chain_id"),
port_id: PortId::from_str("port_id").unwrap(),
channel_id: ChannelId::from_str("channel-07").unwrap(),
key_name: None,
counterparty_key_name: None,
},
ClearPacketsCmd::parse_from([
"test",
"--chain",
"chain_id",
"--port",
"port_id",
"--channel",
"channel-07"
])
)
}
#[test]
fn test_clear_packets_chan_alias() {
assert_eq!(
ClearPacketsCmd {
chain_id: ChainId::from_string("chain_id"),
port_id: PortId::from_str("port_id").unwrap(),
channel_id: ChannelId::from_str("channel-07").unwrap(),
key_name: None,
counterparty_key_name: None
},
ClearPacketsCmd::parse_from([
"test",
"--chain",
"chain_id",
"--port",
"port_id",
"--chan",
"channel-07"
])
)
}
#[test]
fn test_clear_packets_key_name() {
assert_eq!(
ClearPacketsCmd {
chain_id: ChainId::from_string("chain_id"),
port_id: PortId::from_str("port_id").unwrap(),
channel_id: ChannelId::from_str("channel-07").unwrap(),
key_name: Some("key_name".to_owned()),
counterparty_key_name: None,
},
ClearPacketsCmd::parse_from([
"test",
"--chain",
"chain_id",
"--port",
"port_id",
"--channel",
"channel-07",
"--key-name",
"key_name"
])
)
}
#[test]
fn test_clear_packets_counterparty_key_name() {
assert_eq!(
ClearPacketsCmd {
chain_id: ChainId::from_string("chain_id"),
port_id: PortId::from_str("port_id").unwrap(),
channel_id: ChannelId::from_str("channel-07").unwrap(),
key_name: None,
counterparty_key_name: Some("counterparty_key_name".to_owned()),
},
ClearPacketsCmd::parse_from([
"test",
"--chain",
"chain_id",
"--port",
"port_id",
"--channel",
"channel-07",
"--counterparty-key-name",
"counterparty_key_name"
])
)
}
#[test]
fn test_clear_packets_no_chan() {
assert!(ClearPacketsCmd::try_parse_from([
"test", "--chain", "chain_id", "--port", "port_id"
])
.is_err())
}
#[test]
fn test_clear_packets_no_port() {
assert!(ClearPacketsCmd::try_parse_from([
"test",
"--chain",
"chain_id",
"--channel",
"channel-07"
])
.is_err())
}
#[test]
fn test_clear_packets_no_chain() {
assert!(ClearPacketsCmd::try_parse_from([
"test",
"--port",
"port_id",
"--channel",
"channel-07"
])
.is_err())
}
}