ibc_testkit/testapp/ibc/core/
client_ctx.rs1use core::fmt::Debug;
2
3use basecoin_store::context::{ProvableStore, Store};
4use basecoin_store::types::Height as StoreHeight;
5use ibc::core::client::context::{
6 ClientExecutionContext, ClientValidationContext, ExtClientValidationContext,
7};
8use ibc::core::client::types::Height;
9use ibc::core::host::types::error::HostError;
10use ibc::core::host::types::identifiers::{ChannelId, ClientId, PortId};
11use ibc::core::host::types::path::{
12 ClientConsensusStatePath, ClientStatePath, ClientUpdateHeightPath, ClientUpdateTimePath, Path,
13};
14use ibc::core::host::ValidationContext;
15use ibc::core::primitives::Timestamp;
16use ibc::primitives::prelude::*;
17
18use super::types::MockIbcStore;
19use crate::testapp::ibc::clients::mock::client_state::MockClientContext;
20use crate::testapp::ibc::clients::{AnyClientState, AnyConsensusState};
21
22pub type PortChannelIdMap<V> = BTreeMap<PortId, BTreeMap<ChannelId, V>>;
23
24#[derive(Clone, Debug)]
27pub struct MockClientRecord {
28 pub client_state: Option<AnyClientState>,
30
31 pub consensus_states: BTreeMap<Height, AnyConsensusState>,
33}
34
35impl<S> MockClientContext for MockIbcStore<S>
36where
37 S: ProvableStore + Debug,
38{
39 fn host_timestamp(&self) -> Result<Timestamp, HostError> {
40 ValidationContext::host_timestamp(self)
41 }
42
43 fn host_height(&self) -> Result<Height, HostError> {
44 ValidationContext::host_height(self)
45 }
46}
47
48impl<S> ExtClientValidationContext for MockIbcStore<S>
49where
50 S: ProvableStore + Debug,
51{
52 fn host_timestamp(&self) -> Result<Timestamp, HostError> {
53 ValidationContext::host_timestamp(self)
54 }
55
56 fn host_height(&self) -> Result<Height, HostError> {
57 ValidationContext::host_height(self)
58 }
59
60 fn consensus_state_heights(&self, client_id: &ClientId) -> Result<Vec<Height>, HostError> {
62 let path = format!("clients/{}/consensusStates", client_id).into();
63
64 self.consensus_state_store
65 .get_keys(&path)
66 .into_iter()
67 .filter_map(|path| {
68 if let Ok(Path::ClientConsensusState(consensus_path)) = path.try_into() {
69 Some(consensus_path)
70 } else {
71 None
72 }
73 })
74 .map(|consensus_path| {
75 Height::new(
76 consensus_path.revision_number,
77 consensus_path.revision_height,
78 )
79 .map_err(HostError::invalid_state)
80 })
81 .collect::<Result<Vec<_>, _>>()
82 }
83
84 fn next_consensus_state(
85 &self,
86 client_id: &ClientId,
87 height: &Height,
88 ) -> Result<Option<Self::ConsensusStateRef>, HostError> {
89 let path = format!("clients/{client_id}/consensusStates").into();
90
91 let keys = self.store.get_keys(&path);
92 let found_path = keys.into_iter().find_map(|path| {
93 if let Ok(Path::ClientConsensusState(path)) = path.try_into() {
94 if height
95 < &Height::new(path.revision_number, path.revision_height).expect("no error")
96 {
97 return Some(path);
98 }
99 }
100 None
101 });
102
103 let consensus_state = found_path
104 .map(|path| {
105 self.consensus_state_store
106 .get(StoreHeight::Pending, &path)
107 .ok_or_else(|| {
108 HostError::failed_to_retrieve(format!(
109 "consensus state for client `{}` at height `{}`",
110 client_id.clone(),
111 *height
112 ))
113 })
114 })
115 .transpose()
116 .map_err(HostError::missing_state)?;
117
118 Ok(consensus_state)
119 }
120
121 fn prev_consensus_state(
122 &self,
123 client_id: &ClientId,
124 height: &Height,
125 ) -> Result<Option<Self::ConsensusStateRef>, HostError> {
126 let path = format!("clients/{client_id}/consensusStates").into();
127
128 let keys = self.store.get_keys(&path);
129 let found_path = keys.into_iter().rev().find_map(|path| {
130 if let Ok(Path::ClientConsensusState(path)) = path.try_into() {
131 if height
132 > &Height::new(path.revision_number, path.revision_height).expect("no error")
133 {
134 return Some(path);
135 }
136 }
137 None
138 });
139
140 let consensus_state = found_path
141 .map(|path| {
142 self.consensus_state_store
143 .get(StoreHeight::Pending, &path)
144 .ok_or_else(|| {
145 HostError::failed_to_retrieve(format!(
146 "consensus state for client `{}` at height `{}`",
147 client_id.clone(),
148 *height
149 ))
150 })
151 })
152 .transpose()
153 .map_err(HostError::missing_state)?;
154
155 Ok(consensus_state)
156 }
157}
158
159impl<S> ClientValidationContext for MockIbcStore<S>
160where
161 S: ProvableStore + Debug,
162{
163 type ClientStateRef = AnyClientState;
164 type ConsensusStateRef = AnyConsensusState;
165
166 fn client_state(&self, client_id: &ClientId) -> Result<Self::ClientStateRef, HostError> {
167 self.client_state_store
168 .get(StoreHeight::Pending, &ClientStatePath(client_id.clone()))
169 .ok_or(HostError::failed_to_retrieve(format!(
170 "missing client state for client {}",
171 client_id.clone()
172 )))
173 }
174
175 fn consensus_state(
176 &self,
177 client_cons_state_path: &ClientConsensusStatePath,
178 ) -> Result<AnyConsensusState, HostError> {
179 let height = Height::new(
180 client_cons_state_path.revision_number,
181 client_cons_state_path.revision_height,
182 )
183 .map_err(HostError::invalid_state)?;
184 let consensus_state = self
185 .consensus_state_store
186 .get(StoreHeight::Pending, client_cons_state_path)
187 .ok_or(HostError::failed_to_retrieve(format!(
188 "consensus state for client `{}` at height `{}`",
189 client_cons_state_path.client_id.clone(),
190 height
191 )))?;
192
193 Ok(consensus_state)
194 }
195
196 fn client_update_meta(
199 &self,
200 client_id: &ClientId,
201 height: &Height,
202 ) -> Result<(Timestamp, Height), HostError> {
203 let client_update_time_path = ClientUpdateTimePath::new(
204 client_id.clone(),
205 height.revision_number(),
206 height.revision_height(),
207 );
208 let processed_timestamp = self
209 .client_processed_times
210 .get(StoreHeight::Pending, &client_update_time_path)
211 .ok_or(HostError::failed_to_retrieve(format!(
212 "missing client update metadata for client {} at height {}",
213 client_id.clone(),
214 *height,
215 )))?;
216 let client_update_height_path = ClientUpdateHeightPath::new(
217 client_id.clone(),
218 height.revision_number(),
219 height.revision_height(),
220 );
221 let processed_height = self
222 .client_processed_heights
223 .get(StoreHeight::Pending, &client_update_height_path)
224 .ok_or(HostError::failed_to_retrieve(format!(
225 "missing client update metadata for client {} at height {}",
226 client_id.clone(),
227 *height,
228 )))?;
229
230 Ok((processed_timestamp, processed_height))
231 }
232}
233
234impl<S> ClientExecutionContext for MockIbcStore<S>
235where
236 S: ProvableStore + Debug,
237{
238 type ClientStateMut = AnyClientState;
239
240 fn store_client_state(
242 &mut self,
243 client_state_path: ClientStatePath,
244 client_state: Self::ClientStateRef,
245 ) -> Result<(), HostError> {
246 self.client_state_store
247 .set(client_state_path, client_state)
248 .map_err(|e| HostError::failed_to_store(format!("{e:?}")))?;
249
250 Ok(())
251 }
252
253 fn store_consensus_state(
255 &mut self,
256 consensus_state_path: ClientConsensusStatePath,
257 consensus_state: Self::ConsensusStateRef,
258 ) -> Result<(), HostError> {
259 self.consensus_state_store
260 .set(consensus_state_path, consensus_state)
261 .map_err(|e| HostError::failed_to_store(format!("{e:?}")))?;
262 Ok(())
263 }
264
265 fn delete_consensus_state(
266 &mut self,
267 consensus_state_path: ClientConsensusStatePath,
268 ) -> Result<(), HostError> {
269 self.consensus_state_store.delete(consensus_state_path);
270 Ok(())
271 }
272
273 fn delete_update_meta(&mut self, client_id: ClientId, height: Height) -> Result<(), HostError> {
276 let client_update_time_path = ClientUpdateTimePath::new(
277 client_id.clone(),
278 height.revision_number(),
279 height.revision_height(),
280 );
281 self.client_processed_times.delete(client_update_time_path);
282 let client_update_height_path = ClientUpdateHeightPath::new(
283 client_id,
284 height.revision_number(),
285 height.revision_height(),
286 );
287 self.client_processed_heights
288 .delete(client_update_height_path);
289 Ok(())
290 }
291
292 fn store_update_meta(
296 &mut self,
297 client_id: ClientId,
298 height: Height,
299 host_timestamp: Timestamp,
300 host_height: Height,
301 ) -> Result<(), HostError> {
302 let client_update_time_path = ClientUpdateTimePath::new(
303 client_id.clone(),
304 height.revision_number(),
305 height.revision_height(),
306 );
307 self.client_processed_times
308 .set(client_update_time_path, host_timestamp)
309 .map_err(|e| HostError::failed_to_store(format!("{e:?}")))?;
310 let client_update_height_path = ClientUpdateHeightPath::new(
311 client_id,
312 height.revision_number(),
313 height.revision_height(),
314 );
315 self.client_processed_heights
316 .set(client_update_height_path, host_height)
317 .map_err(|e| HostError::failed_to_store(format!("{e:?}")))?;
318 Ok(())
319 }
320}