ibc_client_tendermint/
client_state.rs

1//! This module includes trait implementations for the
2//! `ibc_client_tendermint_types::ClientState` type. Implemented traits include
3//! `ClientStateCommon`, `ClientStateValidation`, and `ClientStateExecution`.
4//!
5//! Note that this crate defines a newtype wrapper around the
6//! `ibc_client_tendermint_types::ClientState` type in order to enable
7//! implementing a foreign trait on a foreign type (i.e. the orphan rule in
8//! Rust). As such, this module also includes some trait implementations that
9//! serve to pass through traits implemented on the wrapped `ClientState` type.
10
11use ibc_client_tendermint_types::proto::v1::ClientState as RawTmClientState;
12use ibc_client_tendermint_types::ClientState as ClientStateType;
13use ibc_core_host::types::error::DecodingError;
14use ibc_primitives::prelude::*;
15use ibc_primitives::proto::{Any, Protobuf};
16
17mod common;
18mod execution;
19mod misbehaviour;
20mod update_client;
21mod validation;
22
23pub use common::*;
24pub use execution::*;
25pub use misbehaviour::*;
26pub use update_client::*;
27pub use validation::*;
28
29/// Newtype wrapper around the `ClientState` type, imported from the
30/// `ibc-client-tendermint-types` crate. This wrapper exists so that we can
31/// bypass Rust's orphan rules and implement traits from
32/// `ibc::core::client::context` on the `ClientState` type.
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34#[derive(Clone, Debug, PartialEq, derive_more::From)]
35pub struct ClientState(ClientStateType);
36
37impl ClientState {
38    pub fn inner(&self) -> &ClientStateType {
39        &self.0
40    }
41}
42
43impl Protobuf<RawTmClientState> for ClientState {}
44
45impl TryFrom<RawTmClientState> for ClientState {
46    type Error = DecodingError;
47
48    fn try_from(raw: RawTmClientState) -> Result<Self, Self::Error> {
49        Ok(Self(ClientStateType::try_from(raw)?))
50    }
51}
52
53impl From<ClientState> for RawTmClientState {
54    fn from(client_state: ClientState) -> Self {
55        client_state.0.into()
56    }
57}
58
59impl Protobuf<Any> for ClientState {}
60
61impl TryFrom<Any> for ClientState {
62    type Error = DecodingError;
63
64    fn try_from(raw: Any) -> Result<Self, Self::Error> {
65        Ok(Self(ClientStateType::try_from(raw)?))
66    }
67}
68
69impl From<ClientState> for Any {
70    fn from(client_state: ClientState) -> Self {
71        client_state.0.into()
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use core::time::Duration;
78
79    use ibc_client_tendermint_types::{
80        AllowUpdate, ClientState as ClientStateType, TrustThreshold,
81    };
82    use ibc_core_client::types::Height;
83    use ibc_core_commitment_types::specs::ProofSpecs;
84    use ibc_core_host::types::identifiers::ChainId;
85
86    use super::*;
87
88    #[derive(Clone, Debug, PartialEq)]
89    pub struct ClientStateParams {
90        pub id: ChainId,
91        pub trust_level: TrustThreshold,
92        pub trusting_period: Duration,
93        pub unbonding_period: Duration,
94        pub max_clock_drift: Duration,
95        pub latest_height: Height,
96        pub proof_specs: ProofSpecs,
97        pub upgrade_path: Vec<String>,
98        pub allow_update: AllowUpdate,
99    }
100
101    #[test]
102    fn client_state_verify_height() {
103        // Define a "default" set of parameters to reuse throughout these tests.
104        let default_params: ClientStateParams = ClientStateParams {
105            id: ChainId::new("ibc-1").unwrap(),
106            trust_level: TrustThreshold::ONE_THIRD,
107            trusting_period: Duration::new(64000, 0),
108            unbonding_period: Duration::new(128_000, 0),
109            max_clock_drift: Duration::new(3, 0),
110            latest_height: Height::new(1, 10).expect("Never fails"),
111            proof_specs: ProofSpecs::cosmos(),
112            upgrade_path: Vec::new(),
113            allow_update: AllowUpdate {
114                after_expiry: false,
115                after_misbehaviour: false,
116            },
117        };
118
119        struct Test {
120            name: String,
121            height: Height,
122            setup: Option<Box<dyn FnOnce(ClientState) -> ClientState>>,
123            want_pass: bool,
124        }
125
126        let tests = vec![
127            Test {
128                name: "Successful height verification".to_string(),
129                height: Height::new(1, 8).expect("Never fails"),
130                setup: None,
131                want_pass: true,
132            },
133            Test {
134                name: "Invalid (too large)  client height".to_string(),
135                height: Height::new(1, 12).expect("Never fails"),
136                setup: None,
137                want_pass: false,
138            },
139        ];
140
141        for test in tests {
142            let p = default_params.clone();
143            let client_state = ClientStateType::new(
144                p.id,
145                p.trust_level,
146                p.trusting_period,
147                p.unbonding_period,
148                p.max_clock_drift,
149                p.latest_height,
150                p.proof_specs,
151                p.upgrade_path,
152                p.allow_update,
153            )
154            .expect("Never fails");
155            let client_state = match test.setup {
156                Some(setup) => (setup)(ClientState(client_state)),
157                _ => ClientState(client_state),
158            };
159            let res = validate_proof_height(client_state.inner(), test.height);
160
161            assert_eq!(
162                test.want_pass,
163                res.is_ok(),
164                "ClientState::validate_proof_height() failed for test {}, \nmsg{:?} with error {:?}",
165                test.name,
166                test.height,
167                res.err(),
168            );
169        }
170    }
171}