ibc_core_client_context/client_state.rs
1//! Defines `ClientState`, the core type to be implemented by light clients
2
3use ibc_core_client_types::error::ClientError;
4use ibc_core_client_types::{Height, Status};
5use ibc_core_commitment_types::commitment::{
6 CommitmentPrefix, CommitmentProofBytes, CommitmentRoot,
7};
8use ibc_core_host_types::identifiers::{ClientId, ClientType};
9use ibc_core_host_types::path::{Path, PathBytes};
10use ibc_primitives::prelude::*;
11use ibc_primitives::proto::Any;
12use ibc_primitives::Timestamp;
13
14use crate::context::{ClientExecutionContext, ClientValidationContext};
15use crate::Convertible;
16
17/// `ClientState` methods needed in both validation and execution.
18///
19/// They do not require access to a client `ValidationContext` nor
20/// `ExecutionContext`.
21pub trait ClientStateCommon: Convertible<Any> {
22 /// Performs basic validation on the `consensus_state`.
23 ///
24 /// Notably, an implementation should verify that it can properly
25 /// deserialize the object into the expected format.
26 fn verify_consensus_state(
27 &self,
28 consensus_state: Any,
29 host_timestamp: &Timestamp,
30 ) -> Result<(), ClientError>;
31
32 /// Type of client associated with this state (eg. Tendermint)
33 fn client_type(&self) -> ClientType;
34
35 /// The latest height the client was updated to
36 fn latest_height(&self) -> Height;
37
38 /// Validate that the client is at a sufficient height
39 fn validate_proof_height(&self, proof_height: Height) -> Result<(), ClientError>;
40
41 /// Verify the upgraded client and consensus states and validate proofs
42 /// against the given root.
43 ///
44 /// NOTE: proof heights are not included, as upgrade to a new revision is
45 /// expected to pass only on the last height committed by the current
46 /// revision. Clients are responsible for ensuring that the planned last
47 /// height of the current revision is somehow encoded in the proof
48 /// verification process. This is to ensure that no premature upgrades
49 /// occur, since upgrade plans committed to by the counterparty may be
50 /// cancelled or modified before the last planned height.
51 fn verify_upgrade_client(
52 &self,
53 upgraded_client_state: Any,
54 upgraded_consensus_state: Any,
55 proof_upgrade_client: CommitmentProofBytes,
56 proof_upgrade_consensus_state: CommitmentProofBytes,
57 root: &CommitmentRoot,
58 ) -> Result<(), ClientError>;
59
60 /// Serializes a given path object into a raw path bytes.
61 ///
62 /// This method provides essential information for IBC modules, enabling
63 /// them to understand how path serialization is performed on the chain this
64 /// light client represents it before passing the path bytes to either
65 /// `verify_membership_raw()` or `verify_non_membership_raw()`.
66 fn serialize_path(&self, path: Path) -> Result<PathBytes, ClientError>;
67
68 /// Verifies a proof of the existence of a value at a given raw path bytes.
69 fn verify_membership_raw(
70 &self,
71 prefix: &CommitmentPrefix,
72 proof: &CommitmentProofBytes,
73 root: &CommitmentRoot,
74 path: PathBytes,
75 value: Vec<u8>,
76 ) -> Result<(), ClientError>;
77
78 /// Verifies a proof of the existence of a value at a given path object.
79 fn verify_membership(
80 &self,
81 prefix: &CommitmentPrefix,
82 proof: &CommitmentProofBytes,
83 root: &CommitmentRoot,
84 path: Path,
85 value: Vec<u8>,
86 ) -> Result<(), ClientError> {
87 let path_bytes = self.serialize_path(path)?;
88 self.verify_membership_raw(prefix, proof, root, path_bytes, value)
89 }
90
91 /// Verifies the absence of a given proof at a given raw path bytes.
92 fn verify_non_membership_raw(
93 &self,
94 prefix: &CommitmentPrefix,
95 proof: &CommitmentProofBytes,
96 root: &CommitmentRoot,
97 path: PathBytes,
98 ) -> Result<(), ClientError>;
99
100 /// Verifies the absence of a given proof at a given path object.
101 fn verify_non_membership(
102 &self,
103 prefix: &CommitmentPrefix,
104 proof: &CommitmentProofBytes,
105 root: &CommitmentRoot,
106 path: Path,
107 ) -> Result<(), ClientError> {
108 let path_bytes = self.serialize_path(path)?;
109 self.verify_non_membership_raw(prefix, proof, root, path_bytes)
110 }
111}
112
113/// `ClientState` methods which require access to the client's validation
114/// context
115///
116/// The generic type `V` enables light client developers to expand the set of
117/// methods available under the [`ClientValidationContext`] trait and use them in
118/// their implementation for validating a client state transition.
119///
120/// ```ignore
121/// impl<V> ClientStateValidation<V> for MyClientState
122/// where
123/// V: ClientValidationContext + MyValidationContext,
124/// {
125/// // `MyValidationContext` methods available
126/// }
127///
128/// trait MyValidationContext {
129/// // My Context methods
130/// }
131/// ```
132pub trait ClientStateValidation<V>: ClientStateCommon
133where
134 V: ClientValidationContext,
135{
136 /// verify_client_message must verify a client_message. A client_message
137 /// could be a Header, Misbehaviour. It must handle each type of
138 /// client_message appropriately. Calls to check_for_misbehaviour,
139 /// update_state, and update_state_on_misbehaviour will assume that the
140 /// content of the client_message has been verified and can be trusted. An
141 /// error should be returned if the client_message fails to verify.
142 fn verify_client_message(
143 &self,
144 ctx: &V,
145 client_id: &ClientId,
146 client_message: Any,
147 ) -> Result<(), ClientError>;
148
149 /// Checks for evidence of a misbehaviour in Header or Misbehaviour type. It
150 /// assumes the client_message has already been verified.
151 fn check_for_misbehaviour(
152 &self,
153 ctx: &V,
154 client_id: &ClientId,
155 client_message: Any,
156 ) -> Result<bool, ClientError>;
157
158 /// Returns the status of the client. Only Active clients are allowed to process packets.
159 fn status(&self, ctx: &V, client_id: &ClientId) -> Result<Status, ClientError>;
160
161 /// Verifies whether the calling (subject) client state matches the substitute
162 /// client state for the purposes of client recovery.
163 ///
164 /// Note that this validation function does not need to perform *all* of the
165 /// validation steps necessary to confirm client recovery. Some checks, such
166 /// as checking that the subject client state's latest height < the substitute
167 /// client's latest height, as well as checking that the subject client is
168 /// inactive and that the substitute client is active, are performed by the
169 /// `validate` function in the `recover_client` module at the ics02-client
170 /// level.
171 ///
172 /// Returns `Ok` if the subject and substitute client states match, `Err` otherwise.
173 fn check_substitute(&self, ctx: &V, substitute_client_state: Any) -> Result<(), ClientError>;
174}
175
176/// `ClientState` methods which require access to the client's
177/// `ExecutionContext`.
178///
179/// The generic type `E` enables light client developers to expand the set of
180/// methods available under the [`ClientExecutionContext`] trait and use them in
181/// their implementation for executing a client state transition.
182pub trait ClientStateExecution<E>: ClientStateValidation<E>
183where
184 E: ClientExecutionContext,
185{
186 /// Initialises the client with the initial client and consensus states.
187 ///
188 /// Most clients will want to call `E::store_client_state` and
189 /// `E::store_consensus_state`.
190 fn initialise(
191 &self,
192 ctx: &mut E,
193 client_id: &ClientId,
194 consensus_state: Any,
195 ) -> Result<(), ClientError>;
196
197 /// Updates and stores as necessary any associated information for an IBC
198 /// client, such as the ClientState and corresponding ConsensusState. Upon
199 /// successful update, a list of consensus heights is returned. It assumes
200 /// the client_message has already been verified.
201 ///
202 /// Note that `header` is the field associated with `UpdateKind::UpdateClient`.
203 ///
204 /// Post-condition: on success, the return value MUST contain at least one
205 /// height.
206 fn update_state(
207 &self,
208 ctx: &mut E,
209 client_id: &ClientId,
210 header: Any,
211 ) -> Result<Vec<Height>, ClientError>;
212
213 /// update_state_on_misbehaviour should perform appropriate state changes on
214 /// a client state given that misbehaviour has been detected and verified
215 fn update_state_on_misbehaviour(
216 &self,
217 ctx: &mut E,
218 client_id: &ClientId,
219 client_message: Any,
220 ) -> Result<(), ClientError>;
221
222 /// Update the client state and consensus state in the store with the upgraded ones.
223 fn update_state_on_upgrade(
224 &self,
225 ctx: &mut E,
226 client_id: &ClientId,
227 upgraded_client_state: Any,
228 upgraded_consensus_state: Any,
229 ) -> Result<Height, ClientError>;
230
231 /// Update the subject client using the `substitute_client_state` in response
232 /// to a successful client recovery.
233 fn update_on_recovery(
234 &self,
235 ctx: &mut E,
236 subject_client_id: &ClientId,
237 substitute_client_state: Any,
238 substitute_consensus_state: Any,
239 ) -> Result<(), ClientError>;
240}
241
242/// Primary client trait. Defines all the methods that clients must implement.
243///
244/// `ClientState` is broken up into 3 separate traits to avoid needing to use
245/// fully qualified syntax for every method call (see ADR 7 for more details).
246/// One only needs to implement [`ClientStateCommon`], [`ClientStateValidation`]
247/// and [`ClientStateExecution`]; a blanket implementation will automatically
248/// implement `ClientState`.
249///
250/// Refer to [`ClientStateValidation`] and [`ClientStateExecution`] to learn
251/// more about what both generic parameters represent.
252pub trait ClientState<V: ClientValidationContext, E: ClientExecutionContext>:
253 Send + Sync + ClientStateCommon + ClientStateValidation<V> + ClientStateExecution<E>
254{
255}
256
257impl<V: ClientValidationContext, E: ClientExecutionContext, T> ClientState<V, E> for T where
258 T: Send + Sync + ClientStateCommon + ClientStateValidation<V> + ClientStateExecution<E>
259{
260}