|
| 1 | +# ADR 010: ENABLE STANDALONE ICS-02 INTEGRATION |
| 2 | + |
| 3 | +## Changelog |
| 4 | + |
| 5 | +- 2024-03-05: Draft Proposed |
| 6 | + |
| 7 | +## Status |
| 8 | + |
| 9 | +Proposed |
| 10 | + |
| 11 | +## Context |
| 12 | + |
| 13 | +In ADR-07, we previously granted light client developers the flexibility to |
| 14 | +introduce custom APIs to the client contexts. This decision was made to |
| 15 | +accommodate potential scenarios where not all methods required by every future |
| 16 | +light client might be present in the extensive |
| 17 | +[`ValidationContext`](https://github.com/cosmos/ibc-rs/blob/4421b0dcd6eab6c914b8c5a7d45817b1edb6b2ce/ibc-core/ics24-host/src/context.rs#L31) |
| 18 | +or |
| 19 | +[`ExecutionContext`](https://github.com/cosmos/ibc-rs/blob/4421b0dcd6eab6c914b8c5a7d45817b1edb6b2ce/ibc-core/ics24-host/src/context.rs#L168) |
| 20 | +traits. |
| 21 | + |
| 22 | +In this ADR, while retaining that capability, we propose a reorganization of |
| 23 | +client-related APIs by consolidating them under the ICS-02 client validation and |
| 24 | +execution contexts. While the main top-level context traits in ICS-24 |
| 25 | +(`ibc-core-host` crate) will still have access to these APIs through the |
| 26 | +existing accessor |
| 27 | +[`get_client_validation_context()`](https://github.com/cosmos/ibc-rs/blob/4421b0dcd6eab6c914b8c5a7d45817b1edb6b2ce/ibc-core/ics24-host/src/context.rs#L38) |
| 28 | +and |
| 29 | +[`get_client_execution_context()`](https://github.com/cosmos/ibc-rs/blob/4421b0dcd6eab6c914b8c5a7d45817b1edb6b2ce/ibc-core/ics24-host/src/context.rs#L170) |
| 30 | +methods. |
| 31 | + |
| 32 | +As a result of this ADR, client relevant methods, namely **`client_state()`** |
| 33 | +and **`consensus_state()`**, will be extracted from the large ICS-24 validation |
| 34 | +and execution context traits. Instead, they will find their new home under the |
| 35 | +ICS-02 |
| 36 | +[`ClientValidationContext`](https://github.com/cosmos/ibc-rs/blob/caee889ad308a4aac3f9905de9cdda76c5533cfb/ibc-core/ics02-client/context/src/context.rs#L14) |
| 37 | +and |
| 38 | +[`ClientExecutionContext`](https://github.com/cosmos/ibc-rs/blob/caee889ad308a4aac3f9905de9cdda76c5533cfb/ibc-core/ics02-client/context/src/context.rs#L32) |
| 39 | +traits. |
| 40 | + |
| 41 | +## Objectives |
| 42 | + |
| 43 | +The primary objective is to enhance the delineation of the APIs that connect |
| 44 | +ibc-rs to the host’s storage, offering a more modular approach that allows |
| 45 | +developers who wish to integrate only the implemented client states under the |
| 46 | +ibc-rs to their IBC infra. In other words, this enhancement does not mandate |
| 47 | +hosts to implement all the IBC layers, such as connection, channel, or any of |
| 48 | +the IBC applications, when only light client support is required. |
| 49 | + |
| 50 | +As a result, this ADR sets the stage for several potential use cases, including: |
| 51 | + |
| 52 | +1. **Streamlines CosmWasm light client implementation** |
| 53 | + - Since the refined structure allows for the independent integration of |
| 54 | + ICS-02 client traits into the storage, it becomes straightforward to |
| 55 | + implement light clients living in ibc-rs as CosmWasm contracts, which |
| 56 | + empowers simpler Wasm client integration with ibc-go driven chains. |
| 57 | + |
| 58 | +2. **Enables client integration with various IBC implementations** |
| 59 | + - Any IBC implementation, whether it is based on ibc-rs or a fork of ibc-rs, |
| 60 | + will have this capability to incorporate the latest light client |
| 61 | + implementations by ibc-rs. So that even if an IBC implementation diverges |
| 62 | + from the ibc-rs framework, hosts can benefit from the latest version of |
| 63 | + light client implementation. This is important from the security |
| 64 | + standpoint, broadens the applicability of ibc-rs, and facilitates |
| 65 | + collaboration with other IBC-adjacent solutions. |
| 66 | + |
| 67 | +As part of this ADR and to minimize breaking changes later, we aim to address |
| 68 | +the following client-relevant API deficiencies as well: |
| 69 | + |
| 70 | +1. **Refining access to validation vs execution methods** |
| 71 | + - So far, unauthorized access to client execution methods [has been |
| 72 | + possible](https://github.com/cosmos/ibc-rs/blob/4421b0dcd6eab6c914b8c5a7d45817b1edb6b2ce/ibc-core/ics24-host/src/context.rs#L33) |
| 73 | + under the `ValidationContext`. This will be rectified to ensure that only |
| 74 | + the `ClientState` validation method is callable within the validation |
| 75 | + contexts. This enhances the precision of access control, aligning it more |
| 76 | + closely with the intended functionality. |
| 77 | + |
| 78 | +2. **Bringing more clarity to how context APIs are related** |
| 79 | + - The ambiguous relationship between the main context APIs at ICS-24, ICS-02 |
| 80 | + client APIs, and client-specific APIs can get cleared. This ADR aims for a |
| 81 | + more transparent relationship between these contexts, making it easier to |
| 82 | + understand their interactions and roles. For instance some client relevant |
| 83 | + methods are located under the client contexts, while others are under the |
| 84 | + main (ICS-24) contexts. |
| 85 | + |
| 86 | +3. **Minimizing associated types’ declarations** |
| 87 | + - Our current APIs pose a challenge by requiring users to introduce multiple |
| 88 | + yet the same associated types like `AnyClientState`, `AnyConsensusState`, |
| 89 | + etc across different layers. A more efficient solution involves |
| 90 | + introducing such types in fewer places, providing better access to the |
| 91 | + traits across different layers. However, it's worth acknowledging that an |
| 92 | + ideal solution might entail utilizing features from forthcoming stable |
| 93 | + versions of Rust. This would enable us to leverage the advantages provided |
| 94 | + by either |
| 95 | + [associated_type_bounds](https://rust-lang.github.io/rfcs/2289-associated-type-bounds.html), |
| 96 | + [associated_type_defaults](https://rust-lang.github.io/rfcs/2532-associated-type-defaults.html), |
| 97 | + or |
| 98 | + [implied_bounds](https://rust-lang.github.io/rfcs/2089-implied-bounds.html) |
| 99 | + capabilities. |
| 100 | + |
| 101 | +## Decision |
| 102 | + |
| 103 | +Here is a high-level diagram that illustrates the optimal boundaries for each |
| 104 | +context trait, specifying the scope of access by each of these contexts. In |
| 105 | +addition, the classification of APIs should be such that they allow the |
| 106 | +independent integration of light client implementations. |
| 107 | + |
| 108 | +<div style="text-align: center;"> |
| 109 | + <img src="./assets/adr010.png" alt="Main components" width="800" height="600"> |
| 110 | +</div> |
| 111 | + |
| 112 | +The primary `ValidationContext` and `ExecutionContext` traits at the ICS-24 host |
| 113 | +level will be restructured as follows, only having sufficient access to |
| 114 | +respective ICS-02 client contexts, without caring about the types or methods |
| 115 | +associated with client or consensus states of counterparty chains. It is |
| 116 | +noteworthy that, to better illustrate the desired outcome from the current |
| 117 | +state, the code snippets below are in the `diff` format. |
| 118 | + |
| 119 | +```diff |
| 120 | +pub trait ValidationContext { |
| 121 | + type V: ClientValidationContext; |
| 122 | ++ type HostClientState: ClientStateValidation<Self::V>; |
| 123 | ++ type HostConsensusState: ConsensusState; |
| 124 | +- type E: ClientExecutionContext; |
| 125 | +- type AnyConsensusState: ConsensusState; |
| 126 | +- type AnyClientState: ClientState<Self::V, Self::E>; |
| 127 | + |
| 128 | + /// Retrieve the context that implements all clients' `ValidationContext`. |
| 129 | + fn get_client_validation_context(&self) -> &Self::V; |
| 130 | + |
| 131 | + // This method will be removed and replaced by a `ClientStateDecoder` trait that will encapsulate the ability to decode a client state from an `Any` |
| 132 | +- fn decode_client_state(&self, client_state: Any) -> Result<Self::AnyClientState, ContextError>; |
| 133 | + |
| 134 | +- fn client_state(&self, client_id: &ClientId) -> Result<Self::AnyClientState, ContextError>; |
| 135 | + |
| 136 | +- fn consensus_state( |
| 137 | +- &self, |
| 138 | +- client_cons_state_path: &ClientConsensusStatePath, |
| 139 | +- ) -> Result<Self::AnyConsensusState, ContextError>; |
| 140 | + |
| 141 | + fn host_consensus_state( |
| 142 | + &self, |
| 143 | + height: &Height, |
| 144 | +- ) -> Result<Self::AnyConsensusState, ContextError>; |
| 145 | ++ ) -> Result<Self::HostConsensusState, ContextError>; |
| 146 | + |
| 147 | + fn validate_self_client( |
| 148 | + &self, |
| 149 | +- client_state_of_host_on_counterparty: Any, |
| 150 | ++ client_state_of_host_on_counterparty: Self::HostClientState, |
| 151 | + ) -> Result<(), ContextError>; |
| 152 | + |
| 153 | + ... // other methods |
| 154 | +} |
| 155 | + |
| 156 | +pub trait ExecutionContext: ValidationContext { |
| 157 | ++ type E: ClientExecutionContext; |
| 158 | + |
| 159 | + /// Retrieve the context that implements all clients' `ExecutionContext`. |
| 160 | + fn get_client_execution_context(&mut self) -> &mut Self::E; |
| 161 | + |
| 162 | + ... // other methods |
| 163 | + |
| 164 | +/// Convenient type aliases |
| 165 | ++ pub type ClientStateRef<Ctx> = |
| 166 | ++ <<Ctx as ValidationContext>::V as ClientValidationContext>::ClientStateRef; |
| 167 | + |
| 168 | ++ pub type ClientStateMut<Ctx> = |
| 169 | ++ <<Ctx as ExecutionContext>::E as ClientExecutionContext>::ClientStateMut; |
| 170 | + |
| 171 | ++ pub type ConsensusStateRef<Ctx> = |
| 172 | ++ <<Ctx as ValidationContext>::V as ClientValidationContext>::ConsensusStateRef; |
| 173 | +} |
| 174 | +``` |
| 175 | + |
| 176 | +We should also highlight that ICS-02 houses various types, APIs and |
| 177 | +implementations essential for enabling **light clients of counterparty chains |
| 178 | +operating on the host chain**. Therefore, the `host_consensus_state()` and |
| 179 | +`validate_self_client()` methods, though initially appearing to be ICS-02 |
| 180 | +specific, play a crucial role in the connection handshake validating receiving |
| 181 | +datagrams against the client and consensus states of the host. |
| 182 | + |
| 183 | +In this ADR, these methods will continue to be housed under the main context |
| 184 | +traits. However, we will explicitly define the accepted types for these methods |
| 185 | +as `HostClientState` and `HostConsensusState`. This refinement aims for more |
| 186 | +clarity and will optimize the decoding process, removing an unnecessary layer of |
| 187 | +decoding during information retrieval. Previously, the decoding process for |
| 188 | +these methods involved obtaining `AnyClientState` and `AnyConsensusState` types |
| 189 | +and then converting them into the concrete `HostClientState` and |
| 190 | +`HostConsensusState` types. |
| 191 | + |
| 192 | +Following the aforementioned points, in the ICS-02 level, the |
| 193 | +`ClientValidationContext` and `ClientExecutionContext` traits will be |
| 194 | +restructured as follows, containing all the client relevant methods and types: |
| 195 | + |
| 196 | +```diff |
| 197 | +pub trait ClientValidationContext: Sized { |
| 198 | + // Given that we will be dropping `decode_client_state()` method, |
| 199 | + // the client state type introduced here should have implemented `TryFrom<Any>` and `Into<Any>` traits. |
| 200 | ++ type ClientStateRef: ClientStateValidation<Self>; |
| 201 | ++ type ConsensusStateRef: ConsensusState; |
| 202 | + |
| 203 | ++ fn client_state(&self, client_id: &ClientId) -> Result<Self::ClientStateRef, ContextError>; |
| 204 | + |
| 205 | ++ fn consensus_state( |
| 206 | ++ &self, |
| 207 | ++ client_cons_state_path: &ClientConsensusStatePath, |
| 208 | ++ ) -> Result<Self::ConsensusStateRef, ContextError>; |
| 209 | + |
| 210 | + fn client_update_meta( |
| 211 | + &self, |
| 212 | + client_id: &ClientId, |
| 213 | + height: &Height, |
| 214 | + ) -> Result<(Timestamp, Height), ContextError>; |
| 215 | +} |
| 216 | + |
| 217 | +pub trait ClientExecutionContext: |
| 218 | ++ ClientValidationContext<ClientStateRef = Self::ClientStateMut> |
| 219 | +{ |
| 220 | +- type V: ClientValidationContext; |
| 221 | +- type AnyClientState: ClientState<Self::V, Self>; |
| 222 | +- type AnyConsensusState: ConsensusState; |
| 223 | ++ type ClientStateMut: ClientStateExecution<Self>; |
| 224 | + |
| 225 | ++ fn client_state_mut(&self, client_id: &ClientId) -> Result<Self::ClientStateMut, ContextError> { |
| 226 | ++ self.client_state(client_id) |
| 227 | ++ } |
| 228 | + |
| 229 | + fn store_client_state( |
| 230 | + &mut self, |
| 231 | + client_state_path: ClientStatePath, |
| 232 | + client_state: Self::ClientStateMut, |
| 233 | + ) -> Result<(), ContextError>; |
| 234 | + |
| 235 | + fn store_consensus_state( |
| 236 | + &mut self, |
| 237 | + consensus_state_path: ClientConsensusStatePath, |
| 238 | + consensus_state: Self::ConsensusStateRef, |
| 239 | + ) -> Result<(), ContextError>; |
| 240 | + |
| 241 | + fn delete_consensus_state( |
| 242 | + &mut self, |
| 243 | + consensus_state_path: ClientConsensusStatePath, |
| 244 | + ) -> Result<(), ContextError>; |
| 245 | + |
| 246 | + fn store_update_meta( |
| 247 | + &mut self, |
| 248 | + client_id: ClientId, |
| 249 | + height: Height, |
| 250 | + host_timestamp: Timestamp, |
| 251 | + host_height: Height, |
| 252 | + ) -> Result<(), ContextError>; |
| 253 | + |
| 254 | + fn delete_update_meta( |
| 255 | + &mut self, |
| 256 | + client_id: ClientId, |
| 257 | + height: Height, |
| 258 | + ) -> Result<(), ContextError>; |
| 259 | +} |
| 260 | +``` |
| 261 | + |
| 262 | +The introduction of the `ClientStateMut` associated type in addition to the |
| 263 | +`ClientStateRef` became necessary to tackle the limitation that the |
| 264 | +`ClientState` retrieved from the regular `client_state()` method provides access |
| 265 | +only to validation methods. However, in `execute` handlers, there are scenarios |
| 266 | +(like |
| 267 | +[here](https://github.com/cosmos/ibc-rs/blob/f272f30e0f773d85a99fc553b75d41f9f768d5c5/ibc-core/ics02-client/src/handler/update_client.rs#L54)) |
| 268 | +where access to the execution methods of the client state is required. We aim to |
| 269 | +simplify the user experience by providing a default implementation, relieving |
| 270 | +users from the need to implement the `client_state_mut` method. |
| 271 | + |
| 272 | +Also, the introduction of `<ClientStateRef = Self::ClientStateMut>` is prompted |
| 273 | +by the need to address the characteristics of concrete `ClientState` |
| 274 | +definitions. For instance, in the case of ICS-07, such as `TmClientState`, the |
| 275 | +struct definition can't be split into two fragments, one for validation and the |
| 276 | +other for execution. Therefore, contexts implementing `ClientExecutionContext` |
| 277 | +must introduce a `ClientStateMut` type the same as `ClientStateRef`. |
| 278 | + |
| 279 | +With the mentioned classification, we can now streamline ICS-07 specific APIs, |
| 280 | +eliminating the requirement for implementing a redundant `consensus_state()` |
| 281 | +method. For the sake of simplification, we can remove the `CommonContext` trait |
| 282 | +and consolidate everything under the `TmValidationContext` as follows: |
| 283 | + |
| 284 | +```diff |
| 285 | ++ /// Enables conversion (`TryInto` and `From`) between the consensus state type |
| 286 | ++ /// used by the host and the one specific to the Tendermint light client, which |
| 287 | ++ /// is `ConsensusStateType`. |
| 288 | ++ pub trait ConsensusStateConverter: |
| 289 | ++ TryInto<ConsensusStateType, Error = ClientError> + From<ConsensusStateType> |
| 290 | ++ { |
| 291 | ++ } |
| 292 | + |
| 293 | ++ impl<C> ConsensusStateConverter for C where |
| 294 | ++ C: TryInto<ConsensusStateType, Error = ClientError> + From<ConsensusStateType> |
| 295 | ++ { |
| 296 | ++ } |
| 297 | + |
| 298 | +- pub trait CommonContext { |
| 299 | +- // methods will be moved to the below `ValidationContext` |
| 300 | +- } |
| 301 | + |
| 302 | +// Client's context required during validation |
| 303 | +pub trait ValidationContext: |
| 304 | ++ ClientValidationContext<ConsensusStateRef = Self::AnyConsensusState> |
| 305 | +{ |
| 306 | ++ type ConversionError: ToString; |
| 307 | ++ type AnyConsensusState: TryInto<TmConsensusState, Error = Self::ConversionError>; |
| 308 | + |
| 309 | ++ fn host_timestamp(&self) -> Result<Timestamp, ContextError>; |
| 310 | + |
| 311 | ++ fn host_height(&self) -> Result<Height, ContextError>; |
| 312 | + |
| 313 | +- fn consensus_state( |
| 314 | +- &self, |
| 315 | +- client_cons_state_path: &ClientConsensusStatePath, |
| 316 | +- ) -> Result<Self::AnyConsensusState, ContextError>; |
| 317 | + |
| 318 | ++ fn consensus_state_heights(&self, client_id: &ClientId) -> Result<Vec<Height>, ContextError>; |
| 319 | + |
| 320 | + fn next_consensus_state( |
| 321 | + &self, |
| 322 | + client_id: &ClientId, |
| 323 | + height: &Height, |
| 324 | + ) -> Result<Option<Self::AnyConsensusState>, ContextError>; |
| 325 | + |
| 326 | + fn prev_consensus_state( |
| 327 | + &self, |
| 328 | + client_id: &ClientId, |
| 329 | + height: &Height, |
| 330 | + ) -> Result<Option<Self::AnyConsensusState>, ContextError>; |
| 331 | +} |
| 332 | + |
| 333 | +-impl<T> ExecutionContext for T where T: CommonContext + ClientExecutionContext {} |
| 334 | ++impl<T> ExecutionContext for T where T: ValidationContext + ClientExecutionContext {} |
| 335 | + |
| 336 | +``` |
| 337 | + |
| 338 | +### Remarks |
| 339 | + |
| 340 | +- We move away from the `decode_client_state()` method. Since per our design, |
| 341 | + users must utilize a `ClientState` type that implements both `TryFrom<Any>` |
| 342 | + and `Into<Any>`, therefore, we can offer a trait called `ClientStateDecoder` |
| 343 | + as follows, making it readily available for users seeking to decode/encode a |
| 344 | + client state from/into the `Any` type: |
| 345 | + |
| 346 | + ```rust |
| 347 | + pub trait ClientStateDecoder: TryFrom<Any, Error = ClientError> + Into<Any> {} |
| 348 | + |
| 349 | + impl<T> ClientStateDecoder for T where T: TryFrom<Any, Error = ClientError> {} |
| 350 | + |
| 351 | + ``` |
| 352 | + |
| 353 | +- We will maintain the `client_counter()` and `increase_client_counter()` |
| 354 | + methods within the main context traits. This stems from the fact that light |
| 355 | + clients do not rely on the relative positions in their processes. |
| 356 | + Additionally, these counters are globally tracked, and only IBC handlers |
| 357 | + invoke these methods for setting or retrieving client identifiers. |
| 358 | + |
| 359 | +## Consequences |
| 360 | + |
| 361 | +### Positive |
| 362 | + |
| 363 | +- Enables easy light client integration without the need for integrating the |
| 364 | + entire IBC stack |
| 365 | +- Establishes a clearer relationship between APIs (ICS-02 <> ICS-24), promoting |
| 366 | + better development practices |
| 367 | +- Eliminates redundant methods/types, enhancing integration efficiency |
| 368 | +- Methods under the client contexts will align more with ibc-go client keepers, |
| 369 | + improving interoperability. |
| 370 | + |
| 371 | +### Negative |
| 372 | + |
| 373 | +- Some challenges may arise in identifying trait bounds during a light client |
| 374 | + implementation. |
| 375 | + - Introducing user-importable type aliases helps mitigate this concern. |
| 376 | +- Additionally, this ADR employs significant breaking changes to the hosts. |
| 377 | + |
| 378 | +## References |
| 379 | + |
| 380 | +- [Issue link](https://github.com/cosmos/ibc-rs/issues/1114) |
0 commit comments