1pub mod client_ctx;
2pub mod custom_ctx;
3
4use core::fmt::Display;
5use std::str::FromStr;
6
7use cosmwasm_std::{Binary, CustomQuery, Deps, DepsMut, Empty, Env, Order, Storage};
8use cw_storage_plus::{Bound, Map};
9use ibc_client_wasm_types::client_state::ClientState as WasmClientState;
10use ibc_core::client::context::client_state::ClientStateCommon;
11use ibc_core::client::types::Height;
12use ibc_core::host::types::error::HostError;
13use ibc_core::host::types::identifiers::ClientId;
14use ibc_core::host::types::path::{
15 ClientStatePath, ClientUpdateHeightPath, ClientUpdateTimePath, ITERATE_CONSENSUS_STATE_PREFIX,
16};
17use ibc_core::primitives::proto::{Any, Protobuf};
18use prost::Message;
19
20use crate::api::ClientType;
21use crate::types::{ContractError, HeightTravel, MigrationPrefix};
22use crate::utils::AnyCodec;
23
24pub const CONSENSUS_STATE_HEIGHT_MAP: Map<(u64, u64), Empty> =
30 Map::new(ITERATE_CONSENSUS_STATE_PREFIX);
31
32pub struct Context<'a, C, Q = Empty>
35where
36 C: ClientType<'a>,
37 Q: CustomQuery,
38 <C::ClientState as TryFrom<Any>>::Error: Display,
39 <C::ConsensusState as TryFrom<Any>>::Error: Display,
40{
41 deps: Option<Deps<'a, Q>>,
42 deps_mut: Option<DepsMut<'a, Q>>,
43 env: Env,
44 client_id: ClientId,
45 checksum: Option<Binary>,
46 migration_prefix: MigrationPrefix,
47 client_type: std::marker::PhantomData<C>,
48}
49
50impl<'a, C, Q> Context<'a, C, Q>
51where
52 C: ClientType<'a>,
53 Q: CustomQuery,
54 <C::ClientState as TryFrom<Any>>::Error: Display,
55 <C::ConsensusState as TryFrom<Any>>::Error: Display,
56{
57 pub fn new_ref(deps: Deps<'a, Q>, env: Env) -> Result<Self, ContractError> {
59 let client_id = ClientId::from_str(env.contract.address.as_str())?;
60
61 Ok(Self {
62 deps: Some(deps),
63 deps_mut: None,
64 env,
65 client_id,
66 checksum: None,
67 migration_prefix: MigrationPrefix::None,
68 client_type: std::marker::PhantomData::<C>,
69 })
70 }
71
72 pub fn new_mut(deps_mut: DepsMut<'a, Q>, env: Env) -> Result<Self, ContractError> {
74 let client_id = ClientId::from_str(env.contract.address.as_str())?;
75
76 Ok(Self {
77 deps: None,
78 deps_mut: Some(deps_mut),
79 env,
80 client_id,
81 checksum: None,
82 migration_prefix: MigrationPrefix::None,
83 client_type: std::marker::PhantomData::<C>,
84 })
85 }
86
87 pub fn env(&self) -> &Env {
89 &self.env
90 }
91
92 pub fn log(&self, msg: &str) -> Option<()> {
94 self.deps.map(|deps| deps.api.debug(msg))
95 }
96
97 pub fn client_id(&self) -> ClientId {
99 self.client_id.clone()
100 }
101
102 pub fn set_checksum(&mut self, checksum: Binary) {
104 self.checksum = Some(checksum);
105 }
106
107 pub fn set_subject_prefix(&mut self) {
109 self.migration_prefix = MigrationPrefix::Subject;
110 }
111
112 pub fn set_substitute_prefix(&mut self) {
114 self.migration_prefix = MigrationPrefix::Substitute;
115 }
116
117 pub fn prefixed_key(&self, key: impl AsRef<[u8]>) -> Vec<u8> {
119 let mut prefixed_key = Vec::new();
120 prefixed_key.extend_from_slice(self.migration_prefix.key());
121 prefixed_key.extend_from_slice(key.as_ref());
122
123 prefixed_key
124 }
125
126 pub fn retrieve(&self, key: impl AsRef<[u8]>) -> Result<Vec<u8>, HostError> {
128 let prefixed_key = self.prefixed_key(key);
129
130 let value =
131 self.storage_ref()
132 .get(prefixed_key.as_ref())
133 .ok_or(HostError::failed_to_retrieve(
134 "key not found upon retrieval",
135 ))?;
136
137 Ok(value)
138 }
139
140 pub fn insert(&mut self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>) {
142 self.storage_mut().set(key.as_ref(), value.as_ref());
143 }
144
145 pub fn remove(&mut self, key: impl AsRef<[u8]>) {
147 self.storage_mut().remove(key.as_ref());
148 }
149
150 pub fn get_heights(&self) -> Result<Vec<Height>, HostError> {
152 CONSENSUS_STATE_HEIGHT_MAP
153 .keys(self.storage_ref(), None, None, Order::Ascending)
154 .map(|deserialized_result| {
155 let (rev_number, rev_height) =
156 deserialized_result.map_err(HostError::failed_to_retrieve)?;
157 Height::new(rev_number, rev_height).map_err(HostError::invalid_state)
158 })
159 .collect()
160 }
161
162 pub fn get_adjacent_height(
165 &self,
166 height: &Height,
167 travel: HeightTravel,
168 ) -> Result<Option<Height>, HostError> {
169 let iterator = match travel {
170 HeightTravel::Prev => CONSENSUS_STATE_HEIGHT_MAP.range(
171 self.storage_ref(),
172 None,
173 Some(Bound::exclusive((
174 height.revision_number(),
175 height.revision_height(),
176 ))),
177 Order::Descending,
178 ),
179 HeightTravel::Next => CONSENSUS_STATE_HEIGHT_MAP.range(
180 self.storage_ref(),
181 Some(Bound::exclusive((
182 height.revision_number(),
183 height.revision_height(),
184 ))),
185 None,
186 Order::Ascending,
187 ),
188 };
189
190 iterator
191 .map(|deserialized_result| {
192 let ((rev_number, rev_height), _) =
193 deserialized_result.map_err(HostError::failed_to_retrieve)?;
194 Height::new(rev_number, rev_height).map_err(HostError::invalid_state)
195 })
196 .next()
197 .transpose()
198 }
199
200 pub fn client_update_time_key(&self, height: &Height) -> Vec<u8> {
202 let client_update_time_path = ClientUpdateTimePath::new(
203 self.client_id(),
204 height.revision_number(),
205 height.revision_height(),
206 );
207
208 client_update_time_path.leaf().into_bytes()
209 }
210
211 pub fn client_update_height_key(&self, height: &Height) -> Vec<u8> {
213 let client_update_height_path = ClientUpdateHeightPath::new(
214 self.client_id(),
215 height.revision_number(),
216 height.revision_height(),
217 );
218
219 client_update_height_path.leaf().into_bytes()
220 }
221
222 pub fn obtain_checksum(&self) -> Result<Binary, HostError> {
224 match &self.checksum {
225 Some(checksum) => Ok(checksum.clone()),
226 None => {
227 let client_state_value = self.retrieve(ClientStatePath::leaf())?;
228
229 let wasm_client_state: WasmClientState =
230 Protobuf::<Any>::decode(client_state_value.as_slice())
231 .map_err(HostError::invalid_state)?;
232
233 Ok(wasm_client_state.checksum.into())
234 }
235 }
236 }
237
238 pub fn encode_client_state(&self, client_state: C::ClientState) -> Result<Vec<u8>, HostError> {
240 let wasm_client_state = WasmClientState {
241 checksum: self.obtain_checksum()?.into(),
242 latest_height: client_state.latest_height(),
243 data: C::ClientState::encode_to_any_vec(client_state),
244 };
245
246 Ok(Any::from(wasm_client_state).encode_to_vec())
247 }
248}
249
250pub trait StorageRef {
251 fn storage_ref(&self) -> &dyn Storage;
252}
253
254impl<'a, C, Q> StorageRef for Context<'a, C, Q>
255where
256 C: ClientType<'a>,
257 Q: CustomQuery,
258 <C::ClientState as TryFrom<Any>>::Error: Display,
259 <C::ConsensusState as TryFrom<Any>>::Error: Display,
260{
261 fn storage_ref(&self) -> &dyn Storage {
262 match self.deps {
263 Some(ref deps) => deps.storage,
264 None => match self.deps_mut {
265 Some(ref deps) => deps.storage,
266 None => panic!("Either deps or deps_mut should be available"),
267 },
268 }
269 }
270}
271
272pub trait StorageMut: StorageRef {
273 fn storage_mut(&mut self) -> &mut dyn Storage;
274}
275
276impl<'a, C, Q> StorageMut for Context<'a, C, Q>
277where
278 C: ClientType<'a>,
279 Q: CustomQuery,
280 <C::ClientState as TryFrom<Any>>::Error: Display,
281 <C::ConsensusState as TryFrom<Any>>::Error: Display,
282{
283 fn storage_mut(&mut self) -> &mut dyn Storage {
284 match self.deps_mut {
285 Some(ref mut deps) => deps.storage,
286 None => panic!("deps_mut should be available"),
287 }
288 }
289}