ibc_core_host_cosmos/
validate_self_client.rs1use core::time::Duration;
2
3use ibc_client_tendermint::types::ClientState as TmClientState;
4use ibc_core_client_types::Height;
5use ibc_core_commitment_types::specs::ProofSpecs;
6use ibc_core_host_types::error::HostError;
7use ibc_core_host_types::identifiers::ChainId;
8use ibc_primitives::prelude::*;
9use tendermint::trust_threshold::TrustThresholdFraction as TendermintTrustThresholdFraction;
10
11pub trait ValidateSelfClientContext {
17 fn validate_self_tendermint_client(
18 &self,
19 client_state_of_host_on_counterparty: TmClientState,
20 ) -> Result<(), HostError> {
21 client_state_of_host_on_counterparty
22 .validate()
23 .map_err(|e| {
24 HostError::invalid_state(format!(
25 "counterparty client state could not be validated: {e}"
26 ))
27 })?;
28
29 if client_state_of_host_on_counterparty.is_frozen() {
30 return Err(HostError::invalid_state("client unexpectedly frozen"));
31 }
32
33 let self_chain_id = self.chain_id();
34
35 if self_chain_id != &client_state_of_host_on_counterparty.chain_id {
36 return Err(HostError::invalid_state(format!(
37 "chain ID: expected `{}`, actual `{}`",
38 self_chain_id, client_state_of_host_on_counterparty.chain_id
39 )));
40 }
41
42 let latest_height = client_state_of_host_on_counterparty.latest_height;
43 let self_revision_number = self_chain_id.revision_number();
44
45 if self_revision_number != latest_height.revision_number() {
46 return Err(HostError::invalid_state(format!(
47 "mismatched client revision numbers; expected `{}`, actual `{}`",
48 self_revision_number,
49 latest_height.revision_number()
50 )));
51 }
52
53 if latest_height >= self.host_current_height() {
54 return Err(HostError::invalid_state(format!(
55 "client latest height `{}` should be less than chain height `{}`",
56 latest_height,
57 self.host_current_height()
58 )));
59 }
60
61 if self.proof_specs() != &client_state_of_host_on_counterparty.proof_specs {
62 return Err(HostError::invalid_state(format!(
63 "client proof specs; expected `{:?}`, actual `{:?}`",
64 self.proof_specs(),
65 client_state_of_host_on_counterparty.proof_specs
66 )));
67 }
68
69 let _ = {
70 let trust_level = client_state_of_host_on_counterparty.trust_level;
71
72 TendermintTrustThresholdFraction::new(
73 trust_level.numerator(),
74 trust_level.denominator(),
75 )
76 .map_err(HostError::invalid_state)?
77 };
78
79 if self.unbonding_period() != client_state_of_host_on_counterparty.unbonding_period {
80 return Err(HostError::invalid_state(format!(
81 "unbonding period; expected `{:?}`, actual `{:?}`",
82 self.unbonding_period(),
83 client_state_of_host_on_counterparty.unbonding_period,
84 )));
85 }
86
87 if client_state_of_host_on_counterparty.unbonding_period
88 < client_state_of_host_on_counterparty.trusting_period
89 {
90 return Err(HostError::invalid_state(format!(
91 "counterparty client state: unbonding period must be greater than trusting period; unbonding period ({:?}) < trusting period ({:?})",
92 client_state_of_host_on_counterparty.unbonding_period,
93 client_state_of_host_on_counterparty.trusting_period
94 )));
95 }
96
97 if !client_state_of_host_on_counterparty.upgrade_path.is_empty()
98 && self.upgrade_path() != client_state_of_host_on_counterparty.upgrade_path
99 {
100 return Err(HostError::invalid_state(format!(
101 "upgrade path; expected `{:?}`, actual `{:?}`",
102 self.upgrade_path(),
103 client_state_of_host_on_counterparty.upgrade_path
104 )));
105 }
106
107 Ok(())
108 }
109
110 fn chain_id(&self) -> &ChainId;
112
113 fn host_current_height(&self) -> Height;
115
116 fn proof_specs(&self) -> &ProofSpecs;
118
119 fn unbonding_period(&self) -> Duration;
121
122 fn upgrade_path(&self) -> &[String];
124}