ibc_client_tendermint/client_state/
common.rs1use core::time::Duration;
2
3use ibc_client_tendermint_types::{client_type as tm_client_type, ClientState as ClientStateType};
4use ibc_core_client::context::client_state::ClientStateCommon;
5use ibc_core_client::context::consensus_state::ConsensusState;
6use ibc_core_client::types::error::{ClientError, UpgradeClientError};
7use ibc_core_client::types::{Height, Status};
8use ibc_core_commitment_types::commitment::{
9 CommitmentPrefix, CommitmentProofBytes, CommitmentRoot,
10};
11use ibc_core_commitment_types::error::CommitmentError;
12use ibc_core_commitment_types::merkle::{MerklePath, MerkleProof};
13use ibc_core_commitment_types::proto::ics23::{HostFunctionsManager, HostFunctionsProvider};
14use ibc_core_commitment_types::specs::ProofSpecs;
15use ibc_core_host::types::identifiers::ClientType;
16use ibc_core_host::types::path::{
17 Path, PathBytes, UpgradeClientStatePath, UpgradeConsensusStatePath,
18};
19use ibc_primitives::prelude::*;
20use ibc_primitives::proto::Any;
21use ibc_primitives::{Timestamp, ToVec};
22
23use super::ClientState;
24use crate::consensus_state::ConsensusState as TmConsensusState;
25
26impl ClientStateCommon for ClientState {
27 fn verify_consensus_state(
28 &self,
29 consensus_state: Any,
30 host_timestamp: &Timestamp,
31 ) -> Result<(), ClientError> {
32 verify_consensus_state(
33 consensus_state,
34 host_timestamp,
35 self.inner().trusting_period,
36 )
37 }
38
39 fn client_type(&self) -> ClientType {
40 tm_client_type()
41 }
42
43 fn latest_height(&self) -> Height {
44 self.0.latest_height
45 }
46
47 fn validate_proof_height(&self, proof_height: Height) -> Result<(), ClientError> {
48 validate_proof_height(self.inner(), proof_height)
49 }
50
51 fn verify_upgrade_client(
52 &self,
53 upgraded_client_state: Any,
54 upgraded_consensus_state: Any,
55 proof_upgrade_client: CommitmentProofBytes,
56 proof_upgrade_consensus_state: CommitmentProofBytes,
57 root: &CommitmentRoot,
58 ) -> Result<(), ClientError> {
59 let last_height = self.latest_height().revision_height();
60
61 let upgrade_path = &self.inner().upgrade_path;
67 let (upgrade_path_prefix, upgrade_path) = match upgrade_path.len() {
68 0 => {
69 return Err(UpgradeClientError::MissingUpgradePath.into());
70 }
71 1 => (CommitmentPrefix::empty(), upgrade_path[0].clone()),
72 2 => (
73 upgrade_path[0].as_bytes().to_vec().into(),
74 upgrade_path[1].clone(),
75 ),
76 _ => {
77 return Err(UpgradeClientError::InvalidUpgradePath {
78 description: "upgrade path is too long".to_string(),
79 }
80 .into());
81 }
82 };
83
84 let upgrade_client_path_bytes =
85 self.serialize_path(Path::UpgradeClientState(UpgradeClientStatePath {
86 upgrade_path: upgrade_path.clone(),
87 height: last_height,
88 }))?;
89
90 let upgrade_consensus_path_bytes =
91 self.serialize_path(Path::UpgradeConsensusState(UpgradeConsensusStatePath {
92 upgrade_path,
93 height: last_height,
94 }))?;
95
96 verify_upgrade_client::<HostFunctionsManager>(
97 self.inner(),
98 upgraded_client_state,
99 upgraded_consensus_state,
100 proof_upgrade_client,
101 proof_upgrade_consensus_state,
102 upgrade_path_prefix,
103 upgrade_client_path_bytes,
104 upgrade_consensus_path_bytes,
105 root,
106 )
107 }
108
109 fn serialize_path(&self, path: Path) -> Result<PathBytes, ClientError> {
110 Ok(path.to_string().into_bytes().into())
111 }
112
113 fn verify_membership_raw(
114 &self,
115 prefix: &CommitmentPrefix,
116 proof: &CommitmentProofBytes,
117 root: &CommitmentRoot,
118 path: PathBytes,
119 value: Vec<u8>,
120 ) -> Result<(), ClientError> {
121 verify_membership::<HostFunctionsManager>(
122 &self.inner().proof_specs,
123 prefix,
124 proof,
125 root,
126 path,
127 value,
128 )
129 }
130
131 fn verify_non_membership_raw(
132 &self,
133 prefix: &CommitmentPrefix,
134 proof: &CommitmentProofBytes,
135 root: &CommitmentRoot,
136 path: PathBytes,
137 ) -> Result<(), ClientError> {
138 verify_non_membership::<HostFunctionsManager>(
139 &self.inner().proof_specs,
140 prefix,
141 proof,
142 root,
143 path,
144 )
145 }
146}
147
148pub fn verify_consensus_state(
155 consensus_state: Any,
156 host_timestamp: &Timestamp,
157 trusting_period: Duration,
158) -> Result<(), ClientError> {
159 let tm_consensus_state = TmConsensusState::try_from(consensus_state)?;
160
161 if tm_consensus_state.root().is_empty() {
162 Err(CommitmentError::MissingCommitmentRoot)?;
163 };
164
165 if consensus_state_status(&tm_consensus_state, host_timestamp, trusting_period)?.is_expired() {
166 return Err(ClientError::InvalidStatus(Status::Expired));
167 }
168
169 Ok(())
170}
171
172pub fn consensus_state_status<CS: ConsensusState>(
175 consensus_state: &CS,
176 host_timestamp: &Timestamp,
177 trusting_period: Duration,
178) -> Result<Status, ClientError> {
179 if let Some(elapsed_since_latest_consensus_state) =
183 host_timestamp.duration_since(&consensus_state.timestamp()?)
184 {
185 if elapsed_since_latest_consensus_state >= trusting_period {
189 return Ok(Status::Expired);
190 }
191 }
192
193 Ok(Status::Active)
194}
195
196pub fn validate_proof_height(
203 client_state: &ClientStateType,
204 proof_height: Height,
205) -> Result<(), ClientError> {
206 let latest_height = client_state.latest_height;
207
208 if latest_height < proof_height {
209 return Err(ClientError::InsufficientProofHeight {
210 actual: latest_height,
211 expected: proof_height,
212 });
213 }
214
215 Ok(())
216}
217
218#[allow(clippy::too_many_arguments)]
230pub fn verify_upgrade_client<H: HostFunctionsProvider>(
231 client_state: &ClientStateType,
232 upgraded_client_state: Any,
233 upgraded_consensus_state: Any,
234 proof_upgrade_client: CommitmentProofBytes,
235 proof_upgrade_consensus_state: CommitmentProofBytes,
236 upgrade_path_prefix: CommitmentPrefix,
237 upgrade_client_path_bytes: PathBytes,
238 upgrade_consensus_path_bytes: PathBytes,
239 root: &CommitmentRoot,
240) -> Result<(), ClientError> {
241 let upgraded_tm_client_state = ClientState::try_from(upgraded_client_state.clone())?;
243
244 TmConsensusState::try_from(upgraded_consensus_state.clone())?;
246
247 let latest_height = client_state.latest_height;
248 let upgraded_tm_client_state_height = upgraded_tm_client_state.latest_height();
249
250 if latest_height >= upgraded_tm_client_state_height {
254 Err(UpgradeClientError::InsufficientUpgradeHeight {
255 upgraded_height: upgraded_tm_client_state_height,
256 client_height: latest_height,
257 })?
258 }
259
260 verify_membership::<H>(
262 &client_state.proof_specs,
263 &upgrade_path_prefix,
264 &proof_upgrade_client,
265 root,
266 upgrade_client_path_bytes,
267 upgraded_client_state.to_vec(),
268 )?;
269
270 verify_membership::<H>(
272 &client_state.proof_specs,
273 &upgrade_path_prefix,
274 &proof_upgrade_consensus_state,
275 root,
276 upgrade_consensus_path_bytes,
277 upgraded_consensus_state.to_vec(),
278 )?;
279
280 Ok(())
281}
282
283pub fn verify_membership<H: HostFunctionsProvider>(
289 proof_specs: &ProofSpecs,
290 prefix: &CommitmentPrefix,
291 proof: &CommitmentProofBytes,
292 root: &CommitmentRoot,
293 path: PathBytes,
294 value: Vec<u8>,
295) -> Result<(), ClientError> {
296 if prefix.is_empty() {
297 Err(CommitmentError::MissingCommitmentPrefix)?;
298 }
299
300 let merkle_path = MerklePath::new(vec![prefix.as_bytes().to_vec().into(), path]);
301
302 let merkle_proof = MerkleProof::try_from(proof)?;
303
304 merkle_proof.verify_membership::<H>(proof_specs, root.clone().into(), merkle_path, value, 0)?;
305
306 Ok(())
307}
308
309pub fn verify_non_membership<H: HostFunctionsProvider>(
315 proof_specs: &ProofSpecs,
316 prefix: &CommitmentPrefix,
317 proof: &CommitmentProofBytes,
318 root: &CommitmentRoot,
319 path: PathBytes,
320) -> Result<(), ClientError> {
321 let merkle_path = MerklePath::new(vec![prefix.as_bytes().to_vec().into(), path]);
322
323 let merkle_proof = MerkleProof::try_from(proof)?;
324
325 merkle_proof.verify_non_membership::<H>(proof_specs, root.clone().into(), merkle_path)?;
326
327 Ok(())
328}