Skip to content

imp(api)!: refactor client relevant APIs to allow independent ICS-02 integration #1115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- [ibc] Refactor client relevant APIs for improved modularity and allow
standalone ICS-02 integration
([\#1114](https://github.com/cosmos/ibc-rs/issues/1114))
1 change: 1 addition & 0 deletions ci/no-std-check/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 27 additions & 28 deletions ibc-clients/ics07-tendermint/src/client_state/execution.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
use ibc_client_tendermint_types::{
ClientState as ClientStateType, ConsensusState as ConsensusStateType, Header as TmHeader,
};
use ibc_core_client::context::client_state::ClientStateExecution;
use ibc_core_client::context::ClientExecutionContext;
use ibc_core_client::context::prelude::*;
use ibc_core_client::types::error::ClientError;
use ibc_core_client::types::Height;
use ibc_core_host::types::identifiers::ClientId;
use ibc_core_host::types::path::{ClientConsensusStatePath, ClientStatePath};
use ibc_core_host::ExecutionContext;
use ibc_primitives::prelude::*;
use ibc_primitives::proto::Any;

use super::ClientState;
use crate::consensus_state::ConsensusState as TmConsensusState;
use crate::context::{CommonContext, ExecutionContext as TmExecutionContext};
use crate::context::{
ExecutionContext as TmExecutionContext, ValidationContext as TmValidationContext,
};

impl<E> ClientStateExecution<E> for ClientState
where
E: TmExecutionContext + ExecutionContext,
<E as ClientExecutionContext>::AnyClientState: From<ClientStateType>,
<E as ClientExecutionContext>::AnyConsensusState: From<ConsensusStateType>,
E: TmExecutionContext,
<E as ClientValidationContext>::ClientStateRef: From<ClientStateType>,
<E as ClientValidationContext>::ConsensusStateRef: From<ConsensusStateType>,
{
fn initialise(
&self,
Expand Down Expand Up @@ -77,12 +77,12 @@ pub fn initialise<E>(
consensus_state: Any,
) -> Result<(), ClientError>
where
E: TmExecutionContext + ExecutionContext,
<E as ClientExecutionContext>::AnyClientState: From<ClientStateType>,
<E as ClientExecutionContext>::AnyConsensusState: From<ConsensusStateType>,
E: TmExecutionContext,
<E as ClientValidationContext>::ClientStateRef: From<ClientStateType>,
<E as ClientValidationContext>::ConsensusStateRef: From<ConsensusStateType>,
{
let host_timestamp = CommonContext::host_timestamp(ctx)?;
let host_height = CommonContext::host_height(ctx)?;
let host_timestamp = TmValidationContext::host_timestamp(ctx)?;
let host_height = TmValidationContext::host_height(ctx)?;

let tm_consensus_state = ConsensusStateType::try_from(consensus_state)?;

Expand Down Expand Up @@ -122,9 +122,9 @@ pub fn update_state<E>(
header: Any,
) -> Result<Vec<Height>, ClientError>
where
E: TmExecutionContext + ExecutionContext,
<E as ClientExecutionContext>::AnyClientState: From<ClientStateType>,
<E as ClientExecutionContext>::AnyConsensusState: From<ConsensusStateType>,
E: TmExecutionContext,
<E as ClientValidationContext>::ClientStateRef: From<ClientStateType>,
<E as ClientValidationContext>::ConsensusStateRef: From<ConsensusStateType>,
{
let header = TmHeader::try_from(header)?;
let header_height = header.height();
Expand All @@ -138,7 +138,7 @@ where
header_height.revision_height(),
);

CommonContext::consensus_state(ctx, &path_at_header_height).ok()
ctx.consensus_state(&path_at_header_height).ok()
};

if maybe_existing_consensus_state.is_some() {
Expand All @@ -147,8 +147,8 @@ where
//
// Do nothing.
} else {
let host_timestamp = CommonContext::host_timestamp(ctx)?;
let host_height = CommonContext::host_height(ctx)?;
let host_timestamp = TmValidationContext::host_timestamp(ctx)?;
let host_height = TmValidationContext::host_height(ctx)?;

let new_consensus_state = ConsensusStateType::from(header.clone());
let new_client_state = client_state.clone().with_header(header)?;
Expand Down Expand Up @@ -189,9 +189,8 @@ pub fn update_on_misbehaviour<E>(
_client_message: Any,
) -> Result<(), ClientError>
where
E: TmExecutionContext + ExecutionContext,
<E as ClientExecutionContext>::AnyClientState: From<ClientStateType>,
<E as ClientExecutionContext>::AnyConsensusState: From<ConsensusStateType>,
E: TmExecutionContext,
<E as ClientValidationContext>::ClientStateRef: From<ClientStateType>,
{
// NOTE: frozen height is set to `Height {revision_height: 0,
// revision_number: 1}` and it is the same for all misbehaviour. This
Expand Down Expand Up @@ -221,9 +220,9 @@ pub fn update_on_upgrade<E>(
upgraded_consensus_state: Any,
) -> Result<Height, ClientError>
where
E: TmExecutionContext + ExecutionContext,
<E as ClientExecutionContext>::AnyClientState: From<ClientStateType>,
<E as ClientExecutionContext>::AnyConsensusState: From<ConsensusStateType>,
E: TmExecutionContext,
<E as ClientValidationContext>::ClientStateRef: From<ClientStateType>,
<E as ClientValidationContext>::ConsensusStateRef: From<ConsensusStateType>,
{
let mut upgraded_tm_client_state = ClientState::try_from(upgraded_client_state)?;
let upgraded_tm_cons_state = TmConsensusState::try_from(upgraded_consensus_state)?;
Expand Down Expand Up @@ -267,8 +266,8 @@ where
);

let latest_height = new_client_state.latest_height;
let host_timestamp = CommonContext::host_timestamp(ctx)?;
let host_height = CommonContext::host_height(ctx)?;
let host_timestamp = TmValidationContext::host_timestamp(ctx)?;
let host_height = TmValidationContext::host_height(ctx)?;

ctx.store_client_state(
ClientStatePath::new(client_id.clone()),
Expand Down Expand Up @@ -301,7 +300,7 @@ pub fn prune_oldest_consensus_state<E>(
client_id: &ClientId,
) -> Result<(), ClientError>
where
E: ClientExecutionContext + CommonContext,
E: ClientExecutionContext + TmValidationContext,
{
let mut heights = ctx.consensus_state_heights(client_id)?;

Expand All @@ -313,7 +312,7 @@ where
height.revision_number(),
height.revision_height(),
);
let consensus_state = CommonContext::consensus_state(ctx, &client_consensus_state_path)?;
let consensus_state = ctx.consensus_state(&client_consensus_state_path)?;
let tm_consensus_state = consensus_state
.try_into()
.map_err(|err| ClientError::Other {
Expand Down
11 changes: 4 additions & 7 deletions ibc-clients/ics07-tendermint/src/client_state/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use ibc_client_tendermint_types::{
TENDERMINT_HEADER_TYPE_URL, TENDERMINT_MISBEHAVIOUR_TYPE_URL,
};
use ibc_core_client::context::client_state::ClientStateValidation;
use ibc_core_client::context::ClientValidationContext;
use ibc_core_client::types::error::ClientError;
use ibc_core_client::types::Status;
use ibc_core_host::types::identifiers::ClientId;
Expand All @@ -20,9 +19,7 @@ use crate::context::{DefaultVerifier, TmVerifier, ValidationContext as TmValidat

impl<V> ClientStateValidation<V> for ClientState
where
V: ClientValidationContext + TmValidationContext,
V::AnyConsensusState: TryInto<TmConsensusState>,
ClientError: From<<V::AnyConsensusState as TryInto<TmConsensusState>>::Error>,
V: TmValidationContext,
{
/// The default verification logic exposed by ibc-rs simply delegates to a
/// standalone `verify_client_message` function. This is to make it as simple
Expand Down Expand Up @@ -74,7 +71,7 @@ pub fn verify_client_message<V>(
verifier: &impl TmVerifier,
) -> Result<(), ClientError>
where
V: ClientValidationContext + TmValidationContext,
V: TmValidationContext,
{
match client_message.type_url.as_str() {
TENDERMINT_HEADER_TYPE_URL => {
Expand Down Expand Up @@ -128,7 +125,7 @@ pub fn check_for_misbehaviour<V>(
client_message: Any,
) -> Result<bool, ClientError>
where
V: ClientValidationContext + TmValidationContext,
V: TmValidationContext,
{
match client_message.type_url.as_str() {
TENDERMINT_HEADER_TYPE_URL => {
Expand All @@ -154,7 +151,7 @@ pub fn status<V>(
client_id: &ClientId,
) -> Result<Status, ClientError>
where
V: ClientValidationContext + TmValidationContext,
V: TmValidationContext,
{
if client_state.is_frozen() {
return Ok(Status::Frozen);
Expand Down
31 changes: 10 additions & 21 deletions ibc-clients/ics07-tendermint/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
use ibc_core_client::context::ClientExecutionContext;
use ibc_core_client::context::{ClientExecutionContext, ClientValidationContext};
use ibc_core_client::types::Height;
use ibc_core_handler_types::error::ContextError;
use ibc_core_host::types::identifiers::ClientId;
use ibc_core_host::types::path::ClientConsensusStatePath;
use ibc_primitives::prelude::*;
use ibc_primitives::Timestamp;
use tendermint_light_client_verifier::ProdVerifier;

use crate::consensus_state::ConsensusState as TmConsensusState;

/// Client's context required during both validation and execution
pub trait CommonContext {
/// Client's context required during validation
pub trait ValidationContext:
ClientValidationContext<ConsensusStateRef = Self::AnyConsensusState>
{
type ConversionError: ToString;
type AnyConsensusState: TryInto<TmConsensusState, Error = Self::ConversionError>;

Expand All @@ -20,43 +21,31 @@ pub trait CommonContext {
/// Returns the current height of the local chain.
fn host_height(&self) -> Result<Height, ContextError>;

/// Retrieve the consensus state for the given client ID at the specified
/// height.
///
/// Returns an error if no such state exists.
fn consensus_state(
&self,
client_cons_state_path: &ClientConsensusStatePath,
) -> Result<Self::AnyConsensusState, ContextError>;

/// Returns all the heights at which a consensus state is stored
fn consensus_state_heights(&self, client_id: &ClientId) -> Result<Vec<Height>, ContextError>;
}

/// Client's context required during validation
pub trait ValidationContext: CommonContext {
/// Search for the lowest consensus state higher than `height`.
fn next_consensus_state(
&self,
client_id: &ClientId,
height: &Height,
) -> Result<Option<Self::AnyConsensusState>, ContextError>;
) -> Result<Option<Self::ConsensusStateRef>, ContextError>;

/// Search for the highest consensus state lower than `height`.
fn prev_consensus_state(
&self,
client_id: &ClientId,
height: &Height,
) -> Result<Option<Self::AnyConsensusState>, ContextError>;
) -> Result<Option<Self::ConsensusStateRef>, ContextError>;
}

/// Client's context required during execution.
///
/// This trait is automatically implemented for all types that implement
/// [`CommonContext`] and [`ClientExecutionContext`]
pub trait ExecutionContext: CommonContext + ClientExecutionContext {}
/// [`ValidationContext`] and [`ClientExecutionContext`]
pub trait ExecutionContext: ValidationContext + ClientExecutionContext {}

impl<T> ExecutionContext for T where T: CommonContext + ClientExecutionContext {}
impl<T> ExecutionContext for T where T: ValidationContext + ClientExecutionContext {}

/// Specifies the Verifier interface that hosts must adhere to when customizing
/// Tendermint client verification behaviour.
Expand Down
16 changes: 13 additions & 3 deletions ibc-core/ics02-client/context/src/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,21 @@ use ibc_core_host_types::path::Path;
use ibc_primitives::prelude::*;
use ibc_primitives::proto::Any;

/// Convenient trait to decode a client state from an `Any` type and obtain a
/// handle to the local instance of `ClientState`.
pub trait ClientStateDecoder: TryFrom<Any, Error = ClientError> {
fn from_any(client_state: Any) -> Result<Self, ClientError> {
Self::try_from(client_state)
}
}

impl<T> ClientStateDecoder for T where T: TryFrom<Any, Error = ClientError> {}

/// `ClientState` methods needed in both validation and execution.
///
/// They do not require access to a client `ValidationContext` nor
/// `ExecutionContext`.
pub trait ClientStateCommon {
pub trait ClientStateCommon: ClientStateDecoder {
/// Performs basic validation on the `consensus_state`.
///
/// Notably, an implementation should verify that it can properly
Expand Down Expand Up @@ -90,7 +100,7 @@ pub trait ClientStateCommon {
/// // My Context methods
/// }
/// ```
pub trait ClientStateValidation<V>
pub trait ClientStateValidation<V>: ClientStateCommon
where
V: ClientValidationContext,
{
Expand Down Expand Up @@ -126,7 +136,7 @@ where
/// The generic type `E` enables light client developers to expand the set of
/// methods available under the [`ClientExecutionContext`] trait and use them in
/// their implementation for executing a client state transition.
pub trait ClientStateExecution<E>
pub trait ClientStateExecution<E>: ClientStateValidation<E>
where
E: ClientExecutionContext,
{
Expand Down
48 changes: 36 additions & 12 deletions ibc-core/ics02-client/context/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,34 @@ use ibc_core_host_types::identifiers::ClientId;
use ibc_core_host_types::path::{ClientConsensusStatePath, ClientStatePath};
use ibc_primitives::Timestamp;

use super::client_state::ClientState;
use super::consensus_state::ConsensusState;
use crate::client_state::{ClientStateExecution, ClientStateValidation};
use crate::consensus_state::ConsensusState;

/// Defines the methods available to clients for validating client state
/// transitions. The generic `V` parameter in
/// [crate::client_state::ClientStateValidation] must
/// inherit from this trait.
pub trait ClientValidationContext {
/// Returns the time and height when the client state for the given
/// [`ClientId`] was updated with a header for the given [`Height`]
fn update_meta(
pub trait ClientValidationContext: Sized {
type ClientStateRef: ClientStateValidation<Self>;
type ConsensusStateRef: ConsensusState;

/// Returns the ClientState for the given identifier `client_id`.
///
/// Note: Clients have the responsibility to store client states on client creation and update.
fn client_state(&self, client_id: &ClientId) -> Result<Self::ClientStateRef, ContextError>;

/// Retrieve the consensus state for the given client ID at the specified
/// height.
///
/// Returns an error if no such state exists.
///
/// Note: Clients have the responsibility to store consensus states on client creation and update.
fn consensus_state(
&self,
client_cons_state_path: &ClientConsensusStatePath,
) -> Result<Self::ConsensusStateRef, ContextError>;

fn client_update_meta(
&self,
client_id: &ClientId,
height: &Height,
Expand All @@ -29,23 +46,30 @@ pub trait ClientValidationContext {
/// Specifically, clients have the responsibility to store their client state
/// and consensus states. This trait defines a uniform interface to do that for
/// all clients.
pub trait ClientExecutionContext: Sized {
type V: ClientValidationContext;
type AnyClientState: ClientState<Self::V, Self>;
type AnyConsensusState: ConsensusState;
pub trait ClientExecutionContext:
ClientValidationContext<ClientStateRef = Self::ClientStateMut>
{
type ClientStateMut: ClientStateExecution<Self>;

/// Returns the ClientState for the given identifier `client_id`.
///
/// Note: Clients have the responsibility to store client states on client creation and update.
fn client_state_mut(&self, client_id: &ClientId) -> Result<Self::ClientStateMut, ContextError> {
self.client_state(client_id)
}

/// Called upon successful client creation and update
fn store_client_state(
&mut self,
client_state_path: ClientStatePath,
client_state: Self::AnyClientState,
client_state: Self::ClientStateMut,
) -> Result<(), ContextError>;

/// Called upon successful client creation and update
fn store_consensus_state(
&mut self,
consensus_state_path: ClientConsensusStatePath,
consensus_state: Self::AnyConsensusState,
consensus_state: Self::ConsensusStateRef,
) -> Result<(), ContextError>;

/// Delete the consensus state from the store located at the given `ClientConsensusStatePath`
Expand Down
7 changes: 7 additions & 0 deletions ibc-core/ics02-client/context/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ pub mod consensus_state;
mod context;
pub use context::*;

/// Trait preludes for the ICS-02 client implementation.
pub mod prelude {
pub use crate::client_state::*;
pub use crate::consensus_state::*;
pub use crate::context::*;
}

pub mod types {
#[doc(inline)]
pub use ibc_core_client_types::*;
Expand Down
Loading