ibc_client_tendermint/client_state/
update_client.rs1use ibc_client_tendermint_types::error::{IntoResult, TendermintClientError};
2use ibc_client_tendermint_types::{ConsensusState as ConsensusStateType, Header as TmHeader};
3use ibc_core_client::context::{Convertible, ExtClientValidationContext};
4use ibc_core_client::types::error::ClientError;
5use ibc_core_client::types::Height;
6use ibc_core_host::types::error::IdentifierError;
7use ibc_core_host::types::identifiers::{ChainId, ClientId};
8use ibc_core_host::types::path::ClientConsensusStatePath;
9use ibc_primitives::prelude::*;
10use ibc_primitives::IntoHostTime;
11use tendermint::crypto::Sha256;
12use tendermint::merkle::MerkleHash;
13use tendermint_light_client_verifier::options::Options;
14use tendermint_light_client_verifier::types::{TrustedBlockState, UntrustedBlockState};
15use tendermint_light_client_verifier::Verifier;
16
17pub fn verify_header<V, H>(
18 ctx: &V,
19 header: &TmHeader,
20 client_id: &ClientId,
21 chain_id: &ChainId,
22 options: &Options,
23 verifier: &impl Verifier,
24) -> Result<(), ClientError>
25where
26 V: ExtClientValidationContext,
27 ConsensusStateType: Convertible<V::ConsensusStateRef>,
28 <ConsensusStateType as TryFrom<V::ConsensusStateRef>>::Error: Into<ClientError>,
29 H: MerkleHash + Sha256 + Default,
30{
31 header.validate_basic::<H>()?;
33
34 header.verify_chain_id_version_matches_height(chain_id)?;
37
38 {
41 let trusted_state = {
42 let trusted_client_cons_state_path = ClientConsensusStatePath::new(
43 client_id.clone(),
44 header.trusted_height.revision_number(),
45 header.trusted_height.revision_height(),
46 );
47 let trusted_consensus_state: ConsensusStateType = ctx
48 .consensus_state(&trusted_client_cons_state_path)?
49 .try_into()
50 .map_err(Into::into)?;
51
52 header.check_trusted_next_validator_set::<H>(
53 &trusted_consensus_state.next_validators_hash,
54 )?;
55
56 TrustedBlockState {
57 chain_id: &chain_id.as_str().try_into().map_err(|e| {
58 IdentifierError::FailedToParse {
59 description: format!("chain ID `{chain_id}`: {e:?}"),
60 }
61 })?,
62 header_time: trusted_consensus_state.timestamp(),
63 height: header
64 .trusted_height
65 .revision_height()
66 .try_into()
67 .map_err(|_| ClientError::FailedToVerifyHeader {
68 description: TendermintClientError::InvalidHeaderHeight(
69 header.trusted_height.revision_height(),
70 )
71 .to_string(),
72 })?,
73 next_validators: &header.trusted_next_validator_set,
74 next_validators_hash: trusted_consensus_state.next_validators_hash,
75 }
76 };
77
78 let untrusted_state = UntrustedBlockState {
79 signed_header: &header.signed_header,
80 validators: &header.validator_set,
81 next_validators: None,
85 };
86
87 let now = ctx.host_timestamp()?.into_host_time()?;
88
89 verifier
91 .verify_update_header(untrusted_state, trusted_state, options, now)
92 .into_result()?;
93 }
94
95 Ok(())
96}
97
98pub fn check_for_misbehaviour_on_update<V>(
101 ctx: &V,
102 header: TmHeader,
103 client_id: &ClientId,
104 client_latest_height: &Height,
105) -> Result<bool, ClientError>
106where
107 V: ExtClientValidationContext,
108 ConsensusStateType: Convertible<V::ConsensusStateRef>,
109 <ConsensusStateType as TryFrom<V::ConsensusStateRef>>::Error: Into<ClientError>,
110{
111 let maybe_existing_consensus_state = {
112 let path_at_header_height = ClientConsensusStatePath::new(
113 client_id.clone(),
114 header.height().revision_number(),
115 header.height().revision_height(),
116 );
117
118 ctx.consensus_state(&path_at_header_height).ok()
119 };
120
121 if let Some(existing_consensus_state) = maybe_existing_consensus_state {
122 let existing_consensus_state: ConsensusStateType =
123 existing_consensus_state.try_into().map_err(Into::into)?;
124
125 let header_consensus_state = ConsensusStateType::from(header);
126
127 Ok(existing_consensus_state != header_consensus_state)
130 } else {
131 {
136 let maybe_prev_cs = ctx.prev_consensus_state(client_id, &header.height())?;
137
138 if let Some(prev_cs) = maybe_prev_cs {
139 let prev_cs: ConsensusStateType = prev_cs.try_into().map_err(Into::into)?;
142
143 if header.signed_header.header().time <= prev_cs.timestamp() {
144 return Ok(true);
145 }
146 }
147 }
148
149 if &header.height() < client_latest_height {
152 let maybe_next_cs = ctx.next_consensus_state(client_id, &header.height())?;
153
154 if let Some(next_cs) = maybe_next_cs {
155 let next_cs: ConsensusStateType = next_cs.try_into().map_err(Into::into)?;
158
159 if header.signed_header.header().time >= next_cs.timestamp() {
160 return Ok(true);
161 }
162 }
163 }
164
165 Ok(false)
166 }
167}