ibc_testkit/fixtures/clients/
tendermint.rs1use core::str::FromStr;
2use core::time::Duration;
3
4use basecoin_store::avl::get_proof_spec as basecoin_proof_spec;
5use bon::Builder;
6use ibc::clients::tendermint::client_state::ClientState as TmClientState;
7use ibc::clients::tendermint::types::error::TendermintClientError;
8use ibc::clients::tendermint::types::proto::v1::{ClientState as RawTmClientState, Fraction};
9#[cfg(feature = "serde")]
10use ibc::clients::tendermint::types::Header;
11use ibc::clients::tendermint::types::{
12 AllowUpdate, ClientState as ClientStateType, TrustThreshold,
13};
14use ibc::core::client::types::proto::v1::Height as RawHeight;
15use ibc::core::client::types::Height;
16use ibc::core::commitment_types::specs::ProofSpecs;
17use ibc::core::host::types::error::DecodingError;
18use ibc::core::host::types::identifiers::ChainId;
19use ibc::core::primitives::prelude::*;
20use tendermint::block::Header as TmHeader;
21
22pub fn dummy_tm_client_state_from_raw(
24 frozen_height: RawHeight,
25) -> Result<TmClientState, DecodingError> {
26 ClientStateType::try_from(dummy_raw_tm_client_state(frozen_height)).map(TmClientState::from)
27}
28
29pub fn dummy_tm_client_state_from_header(tm_header: TmHeader) -> TmClientState {
31 let chain_id = ChainId::from_str(tm_header.chain_id.as_str()).expect("Never fails");
32 let client_state = ClientStateType::new(
33 chain_id.clone(),
34 TrustThreshold::ONE_THIRD,
35 Duration::from_secs(64000),
36 Duration::from_secs(128_000),
37 Duration::from_millis(3000),
38 Height::new(chain_id.revision_number(), u64::from(tm_header.height)).expect("Never fails"),
39 ProofSpecs::cosmos(),
40 Vec::new(),
41 AllowUpdate {
42 after_expiry: false,
43 after_misbehaviour: false,
44 },
45 )
46 .expect("Never fails");
47
48 TmClientState::from(client_state)
49}
50
51pub fn dummy_raw_tm_client_state(frozen_height: RawHeight) -> RawTmClientState {
53 #[allow(deprecated)]
54 RawTmClientState {
55 chain_id: ChainId::new("ibc-0").expect("Never fails").to_string(),
56 trust_level: Some(Fraction {
57 numerator: 1,
58 denominator: 3,
59 }),
60 trusting_period: Some(Duration::from_secs(64000).try_into().expect("no error")),
61 unbonding_period: Some(Duration::from_secs(128_000).try_into().expect("no error")),
62 max_clock_drift: Some(Duration::from_millis(3000).try_into().expect("no error")),
63 latest_height: Some(Height::new(0, 10).expect("Never fails").into()),
64 proof_specs: ProofSpecs::cosmos().into(),
65 upgrade_path: Vec::new(),
66 frozen_height: Some(frozen_height),
67 allow_update_after_expiry: false,
68 allow_update_after_misbehaviour: false,
69 }
70}
71
72#[derive(Debug, Builder)]
73pub struct ClientStateConfig {
74 #[builder(default = TrustThreshold::ONE_THIRD)]
75 pub trust_level: TrustThreshold,
76 #[builder(default = Duration::from_secs(64000))]
77 pub trusting_period: Duration,
78 #[builder(default = Duration::from_secs(128_000))]
79 pub unbonding_period: Duration,
80 #[builder(default = Duration::from_millis(3000))]
81 pub max_clock_drift: Duration,
82 #[builder(default = vec![basecoin_proof_spec(); 2].try_into().expect("no error"))]
83 pub proof_specs: ProofSpecs,
84 #[builder(default)]
85 pub upgrade_path: Vec<String>,
86 #[builder(default = AllowUpdate { after_expiry: false, after_misbehaviour: false })]
87 allow_update: AllowUpdate,
88}
89
90impl Default for ClientStateConfig {
91 fn default() -> Self {
92 Self::builder().build()
93 }
94}
95
96impl ClientStateConfig {
97 pub fn into_client_state(
98 self,
99 chain_id: ChainId,
100 latest_height: Height,
101 ) -> Result<TmClientState, TendermintClientError> {
102 Ok(ClientStateType::new(
103 chain_id,
104 self.trust_level,
105 self.trusting_period,
106 self.unbonding_period,
107 self.max_clock_drift,
108 latest_height,
109 self.proof_specs,
110 self.upgrade_path,
111 self.allow_update,
112 )?
113 .into())
114 }
115}
116
117#[cfg(feature = "serde")]
118pub fn dummy_valid_tendermint_header() -> tendermint::block::Header {
119 use tendermint::block::signed_header::SignedHeader;
120
121 serde_json::from_str::<SignedHeader>(include_str!(concat!(
122 env!("CARGO_MANIFEST_DIR"),
123 "/src/data/json/valid_signed_header.json"
124 )))
125 .expect("Never fails")
126 .header
127}
128
129#[cfg(feature = "serde")]
130pub fn dummy_expired_tendermint_header() -> tendermint::block::Header {
131 use tendermint::block::signed_header::SignedHeader;
132
133 serde_json::from_str::<SignedHeader>(include_str!(concat!(
134 env!("CARGO_MANIFEST_DIR"),
135 "/src/data/json/expired_signed_header.json"
136 )))
137 .expect("Never fails")
138 .header
139}
140
141#[cfg(feature = "serde")]
156pub fn dummy_ics07_header() -> Header {
157 use subtle_encoding::hex;
158 use tendermint::block::signed_header::SignedHeader;
159 use tendermint::validator::{Info as ValidatorInfo, Set as ValidatorSet};
160 use tendermint::PublicKey;
161
162 let shdr = serde_json::from_str::<SignedHeader>(include_str!(concat!(
164 env!("CARGO_MANIFEST_DIR"),
165 "/src/data/json/valid_signed_header.json"
166 )))
167 .expect("Never fails");
168
169 let v1 = ValidatorInfo::new(
172 PublicKey::from_raw_ed25519(
173 &hex::decode_upper("F349539C7E5EF7C49549B09C4BFC2335318AB0FE51FBFAA2433B4F13E816F4A7")
174 .expect("Never fails"),
175 )
176 .expect("Never fails"),
177 281_815_u64.try_into().expect("Never fails"),
178 );
179
180 let vs = ValidatorSet::new(vec![v1.clone()], Some(v1));
181
182 Header {
183 signed_header: shdr,
184 validator_set: vs.clone(),
185 trusted_height: Height::min(0),
186 trusted_next_validator_set: vs,
187 }
188}
189
190#[cfg(all(test, feature = "serde"))]
191mod tests {
192 use ibc::primitives::proto::Any;
193 use rstest::rstest;
194
195 use super::*;
196
197 #[rstest]
198 #[case::valid_client(0, 0, false)]
200 #[case::frozen_client(0, 1, true)]
202 fn tm_client_state_conversions_healthy(
203 #[case] revision_number: u64,
204 #[case] revision_height: u64,
205 #[case] is_frozen: bool,
206 ) {
207 let frozen_height = RawHeight {
208 revision_number,
209 revision_height,
210 };
211
212 let tm_client_state_from_raw = dummy_tm_client_state_from_raw(frozen_height);
214 assert!(tm_client_state_from_raw.is_ok());
215
216 let any_from_tm_client_state = Any::from(
217 tm_client_state_from_raw
218 .as_ref()
219 .expect("Never fails")
220 .clone(),
221 );
222 let tm_client_state_from_any = ClientStateType::try_from(any_from_tm_client_state);
223 assert!(tm_client_state_from_any.is_ok());
224 assert_eq!(
225 Some(is_frozen),
226 tm_client_state_from_any
227 .as_ref()
228 .map(|x| x.is_frozen())
229 .ok()
230 );
231 assert_eq!(
232 tm_client_state_from_raw.expect("Never fails"),
233 tm_client_state_from_any.expect("Never fails").into()
234 );
235 }
236
237 #[test]
238 fn tm_client_state_from_header_healthy() {
239 let tm_header = dummy_valid_tendermint_header();
241 let tm_client_state_from_header = dummy_tm_client_state_from_header(tm_header);
242 let any_from_header = Any::from(tm_client_state_from_header.clone());
243 let tm_client_state_from_any = ClientStateType::try_from(any_from_header);
244 assert!(tm_client_state_from_any.is_ok());
245 assert_eq!(
246 tm_client_state_from_header,
247 tm_client_state_from_any.expect("Never fails").into()
248 );
249 }
250}