hermes_cli/commands/client/
update.rs

1use hermes_cli_components::traits::build::CanLoadBuilder;
2use hermes_cli_framework::command::CommandRunner;
3use hermes_cli_framework::output::Output;
4use hermes_cosmos_relayer::contexts::chain::CosmosChain;
5use hermes_relayer_components::build::traits::builders::relay_builder::CanBuildRelay;
6use hermes_relayer_components::chain::traits::queries::chain_status::CanQueryChainHeight;
7use hermes_relayer_components::chain::traits::queries::client_state::CanQueryClientStateWithLatestHeight;
8use hermes_relayer_components::multi::types::index::Twindex;
9use hermes_relayer_components::relay::traits::target::SourceTarget;
10use hermes_relayer_components::relay::traits::update_client_message_builder::CanSendTargetUpdateClientMessage;
11use ibc_relayer_types::core::ics02_client::height::Height;
12use ibc_relayer_types::core::ics24_host::identifier::{ChainId, ClientId};
13use oneline_eyre::eyre::Context;
14use tracing::info;
15
16use crate::contexts::app::HermesApp;
17use crate::impls::error_wrapper::ErrorWrapper;
18use crate::Result;
19
20#[derive(Debug, clap::Parser)]
21pub struct ClientUpdate {
22    #[clap(
23        long = "host-chain",
24        required = true,
25        value_name = "HOST_CHAIN_ID",
26        help_heading = "REQUIRED",
27        help = "Identifier of the chain that hosts the client"
28    )]
29    host_chain_id: ChainId,
30
31    #[clap(
32        long = "client",
33        required = true,
34        value_name = "CLIENT_ID",
35        help_heading = "REQUIRED",
36        help = "Identifier of the client to update"
37    )]
38    client_id: ClientId,
39
40    #[clap(
41        long = "height",
42        value_name = "HEIGHT",
43        help = "The target height of the client update. Leave unspecified for latest height."
44    )]
45    target_height: Option<u64>,
46}
47
48impl CommandRunner<HermesApp> for ClientUpdate {
49    async fn run(&self, app: &HermesApp) -> Result<Output> {
50        let builder = app.load_builder().await?;
51
52        let host_chain = builder.build_chain(&self.host_chain_id).await?;
53
54        let client_state =
55            <CosmosChain as CanQueryClientStateWithLatestHeight<CosmosChain>>::
56            query_client_state_with_latest_height(&host_chain, &self.client_id)
57            .await?;
58
59        let reference_chain_id = client_state.chain_id;
60        let reference_chain = builder.build_chain(&reference_chain_id).await?;
61
62        let relayer = builder
63            .build_relay(
64                Twindex::<0, 1>,
65                &self.host_chain_id,
66                &reference_chain_id,
67                &self.client_id,
68                &self.client_id, // nothing to pass here
69            )
70            .await?;
71
72        let target_height = match self.target_height {
73            Some(height) => {
74                let height = Height::new(reference_chain_id.version(), height)
75                    .wrap_err("Invalid value for --target-height")?;
76
77                info!("Updating client using specified target height: {height}");
78                height
79            }
80            None => {
81                let height = reference_chain.query_chain_height().await?;
82
83                info!("Updating client using specified target height: {height}");
84                height
85            }
86        };
87
88        relayer
89            .send_target_update_client_messages(SourceTarget, &target_height)
90            .await
91            .wrap_error("Failed to send update client message")?;
92
93        Ok(Output::success_msg("Client successfully updated!"))
94    }
95}