Skip to content

Commit

Permalink
feat: implement ibc-client-cw + ibc-client-tendermint-cw
Browse files Browse the repository at this point in the history
  • Loading branch information
Farhad-Shabani committed Apr 15, 2024
1 parent d5e3ba3 commit 9a24f3c
Show file tree
Hide file tree
Showing 21 changed files with 1,434 additions and 3 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ members = [
"ibc-clients/ics07-tendermint/types",
"ibc-clients/ics07-tendermint",
"ibc-clients/ics08-wasm/types",
"ibc-clients/cosmwasm",
"ibc-clients/ics07-tendermint/cw",
"ibc-clients",
"ibc-apps/ics20-transfer/types",
"ibc-apps/ics20-transfer",
Expand Down Expand Up @@ -53,6 +55,7 @@ authors = ["Informal Systems <[email protected]>"]
base64 = { version = "0.21", default-features = false }
borsh = { version = "0.10", default-features = false }
displaydoc = { version = "0.2", default-features = false }
prost = { version = "0.12", default-features = false }
derive_more = { version = "0.99.17", default-features = false, features = ["from", "into", "display", "try_into"] }
rstest = "0.18.2"
schemars = { version = "0.8.15" }
Expand All @@ -76,6 +79,7 @@ ibc-core-host = { version = "0.51.0", path = "./ibc-core/ics24-host", de
ibc-core-handler = { version = "0.51.0", path = "./ibc-core/ics25-handler", default-features = false }
ibc-core-router = { version = "0.51.0", path = "./ibc-core/ics26-routing", default-features = false }
ibc-client-tendermint = { version = "0.51.0", path = "./ibc-clients/ics07-tendermint", default-features = false }
ibc-client-cw = { version = "0.51.0", path = "./ibc-clients/cosmwasm", default-features = false }
ibc-app-transfer = { version = "0.51.0", path = "./ibc-apps/ics20-transfer", default-features = false }
ibc-app-nft-transfer = { version = "0.51.0", path = "./ibc-apps/ics721-nft-transfer", default-features = false }

Expand Down
9 changes: 7 additions & 2 deletions ibc-clients/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@ IBC light clients:

### ICS-07: Tendermint Light Client

- [ibc-client-tendermint](./ics07-tendermint)
- [ibc-client-tendermint-types](./ics07-tendermint/types)
- [ibc-client-tendermint-types](./ics07-tendermint/types): Data Structures
- [ibc-client-tendermint](./ics07-tendermint): Implementation
- [ibc-client-tendermint-cw](./ics07-tendermint/cw): CosmWasm Contract

### ICS-08: WASM Proxy Light Client

- [ibc-client-wasm-types](./ics08-wasm/types)

### CosmWasm Integration

- [ibc-client-cw](./cosmwasm): Types and Utilities for CosmWasm Integration

## Third-party Clients

Here, we list IBC third-party clients that are compatible with `ibc-rs`. You
Expand Down
40 changes: 40 additions & 0 deletions ibc-clients/cosmwasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
name = "ibc-client-cw"
authors = { workspace = true }
edition = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
rust-version = { workspace = true }
version = { workspace = true }
keywords = ["ibc", "light-client", "CosmWasm"]
readme = "./../README.md"
description = """
This repository contains definitions and utilities that integrate a light client,
built using ibc-rs, into CosmWasm. It functions as a library, allowing users to import
a ready-made `Context` object that is generic across light clients. Users can then
install their concrete client type and integrate it into the contract's entrypoint.
"""

[dependencies]
# external dependencies
derive_more = { workspace = true }
prost = { workspace = true }
serde = { workspace = true, features = ["derive"] }

# ibc dependencies
ibc-core = { workspace = true }
ibc-client-wasm-types = { workspace = true, features = ["cosmwasm"] }

# cosmwasm dependencies
cosmwasm-schema = "1.4.1"
cosmwasm-std = "1.4.1"

[features]
default = ["std"]
std = [
"prost/std",
"serde/std",
"ibc-core/std",
"ibc-client-wasm-types/std",
]

13 changes: 13 additions & 0 deletions ibc-clients/cosmwasm/src/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use ibc_core::client::context::client_state::ClientStateExecution;
use ibc_core::client::context::consensus_state::ConsensusState as ConsensusStateTrait;
use ibc_core::client::types::error::ClientError;
use ibc_core::primitives::proto::Any;

use crate::context::Context;

/// Enables the introduction of custom client and consensus state types tailored
/// for Sovereign light clients.
pub trait ClientType<'a>: Sized {
type ClientState: ClientStateExecution<Context<'a, Self>> + Clone;
type ConsensusState: ConsensusStateTrait + Into<Any> + TryFrom<Any, Error = ClientError>;
}
177 changes: 177 additions & 0 deletions ibc-clients/cosmwasm/src/context/client_ctx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
use ibc_client_wasm_types::client_state::ClientState as WasmClientState;
use ibc_client_wasm_types::consensus_state::ConsensusState as WasmConsensusState;
use ibc_core::client::context::{ClientExecutionContext, ClientValidationContext};
use ibc_core::client::types::error::ClientError;
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::{iteration_key, ClientConsensusStatePath, ClientStatePath};
use ibc_core::primitives::proto::{Any, Protobuf};
use ibc_core::primitives::Timestamp;

use super::Context;
use crate::api::ClientType;
use crate::utils::AnyCodec;

impl<'a, C: ClientType<'a>> ClientValidationContext for Context<'a, C> {
type ClientStateRef = C::ClientState;
type ConsensusStateRef = C::ConsensusState;

fn client_state(&self, _client_id: &ClientId) -> Result<Self::ClientStateRef, ContextError> {
let client_state_value = self.retrieve(ClientStatePath::leaf())?;

Check warning on line 21 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L20-L21

Added lines #L20 - L21 were not covered by tests

let any_wasm: WasmClientState = Protobuf::<Any>::decode(client_state_value.as_slice())
.map_err(|e| ClientError::Other {
description: e.to_string(),
})?;

Check warning on line 26 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L23-L26

Added lines #L23 - L26 were not covered by tests

let sov_client_state = C::ClientState::decode_thru_any(any_wasm.data)?;

Check warning on line 28 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L28

Added line #L28 was not covered by tests

Ok(sov_client_state)
}

Check warning on line 31 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L30-L31

Added lines #L30 - L31 were not covered by tests

fn consensus_state(
&self,
client_cons_state_path: &ClientConsensusStatePath,
) -> Result<Self::ConsensusStateRef, ContextError> {
let consensus_state_value = self.retrieve(client_cons_state_path.leaf())?;

Check warning on line 37 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L33-L37

Added lines #L33 - L37 were not covered by tests

let any_wasm: WasmConsensusState =
C::ConsensusState::decode_thru_any(consensus_state_value)?;

Check warning on line 40 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L39-L40

Added lines #L39 - L40 were not covered by tests

let consensus_state = C::ConsensusState::decode_thru_any(any_wasm.data)?;

Check warning on line 42 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L42

Added line #L42 was not covered by tests

Ok(consensus_state)
}

Check warning on line 45 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L44-L45

Added lines #L44 - L45 were not covered by tests

fn client_update_meta(
&self,
_client_id: &ClientId,
height: &Height,
) -> Result<(Timestamp, Height), ContextError> {
let time_key = self.client_update_time_key(height);

Check warning on line 52 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L47-L52

Added lines #L47 - L52 were not covered by tests

let time_vec = self.retrieve(time_key)?;

Check warning on line 54 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L54

Added line #L54 was not covered by tests

let time = u64::from_be_bytes(time_vec.try_into().expect("invalid timestamp"));

Check warning on line 56 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L56

Added line #L56 was not covered by tests

let timestamp =
Timestamp::from_nanoseconds(time).map_err(ClientError::InvalidPacketTimestamp)?;

Check warning on line 59 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L58-L59

Added lines #L58 - L59 were not covered by tests

let height_key = self.client_update_height_key(height);

Check warning on line 61 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L61

Added line #L61 was not covered by tests

let revision_height_vec = self.retrieve(height_key)?;

Check warning on line 63 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L63

Added line #L63 was not covered by tests

let revision_height =
u64::from_be_bytes(revision_height_vec.try_into().expect("invalid height"));

Check warning on line 66 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L65-L66

Added lines #L65 - L66 were not covered by tests

let height = Height::new(0, revision_height)?;

Check warning on line 68 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L68

Added line #L68 was not covered by tests

Ok((timestamp, height))
}

Check warning on line 71 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L70-L71

Added lines #L70 - L71 were not covered by tests
}

impl<'a, C: ClientType<'a>> ClientExecutionContext for Context<'a, C> {
type ClientStateMut = C::ClientState;

fn store_client_state(
&mut self,
_client_state_path: ClientStatePath,
client_state: Self::ClientStateMut,
) -> Result<(), ContextError> {
let prefixed_key = self.prefixed_key(ClientStatePath::leaf());

Check warning on line 82 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L77-L82

Added lines #L77 - L82 were not covered by tests

let encoded_client_state = self.encode_client_state(client_state)?;

Check warning on line 84 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L84

Added line #L84 was not covered by tests

self.insert(prefixed_key, encoded_client_state);

Ok(())
}

Check warning on line 89 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L86-L89

Added lines #L86 - L89 were not covered by tests

fn store_consensus_state(
&mut self,
consensus_state_path: ClientConsensusStatePath,
consensus_state: Self::ConsensusStateRef,
) -> Result<(), ContextError> {
let prefixed_key = self.prefixed_key(consensus_state_path.leaf());

let encoded_consensus_state = C::ConsensusState::encode_thru_any(consensus_state);

let wasm_consensus_state = WasmConsensusState {
data: encoded_consensus_state,
};

let encoded_wasm_consensus_state = C::ConsensusState::encode_thru_any(wasm_consensus_state);

self.insert(prefixed_key, encoded_wasm_consensus_state);

Ok(())
}

Check warning on line 109 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L91-L109

Added lines #L91 - L109 were not covered by tests

fn delete_consensus_state(
&mut self,
consensus_state_path: ClientConsensusStatePath,
) -> Result<(), ContextError> {
let prefixed_key = self.prefixed_key(consensus_state_path.leaf());

self.remove(prefixed_key);

Ok(())
}

Check warning on line 120 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L111-L120

Added lines #L111 - L120 were not covered by tests

fn store_update_meta(
&mut self,
_client_id: ClientId,
height: Height,
host_timestamp: Timestamp,
host_height: Height,
) -> Result<(), ContextError> {
let time_key = self.client_update_time_key(&height);

let prefixed_time_key = self.prefixed_key(time_key);

let time_vec: [u8; 8] = host_timestamp.nanoseconds().to_be_bytes();

self.insert(prefixed_time_key, time_vec);

let height_key = self.client_update_height_key(&height);

let prefixed_height_key = self.prefixed_key(height_key);

let revision_height_vec: [u8; 8] = host_height.revision_height().to_be_bytes();

self.insert(prefixed_height_key, revision_height_vec);

let iteration_key = iteration_key(height.revision_number(), height.revision_height());

let height_vec = height.to_string().into_bytes();

self.insert(iteration_key, height_vec);

Ok(())
}

Check warning on line 152 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L122-L152

Added lines #L122 - L152 were not covered by tests

fn delete_update_meta(
&mut self,
_client_id: ClientId,
height: Height,
) -> Result<(), ContextError> {
let time_key = self.client_update_time_key(&height);

let prefixed_time_key = self.prefixed_key(time_key);

self.remove(prefixed_time_key);

let height_key = self.client_update_height_key(&height);

let prefixed_height_key = self.prefixed_key(height_key);

self.remove(prefixed_height_key);

let iteration_key = iteration_key(height.revision_number(), height.revision_height());

self.remove(iteration_key);

Ok(())
}

Check warning on line 176 in ibc-clients/cosmwasm/src/context/client_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/client_ctx.rs#L154-L176

Added lines #L154 - L176 were not covered by tests
}
71 changes: 71 additions & 0 deletions ibc-clients/cosmwasm/src/context/custom_ctx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use ibc_core::client::context::prelude::*;
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_core::primitives::Timestamp;

use super::Context;
use crate::api::ClientType;
use crate::types::HeightTravel;

impl<'a, C: ClientType<'a>> ExtClientValidationContext for Context<'a, C> {
fn host_timestamp(&self) -> Result<Timestamp, ContextError> {
let time = self.env().block.time;

let host_timestamp = Timestamp::from_nanoseconds(time.nanos()).expect("invalid timestamp");

Ok(host_timestamp)
}

Check warning on line 19 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L13-L19

Added lines #L13 - L19 were not covered by tests

fn host_height(&self) -> Result<Height, ContextError> {
let host_height = Height::new(0, self.env().block.height)?;

Check warning on line 22 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L21-L22

Added lines #L21 - L22 were not covered by tests

Ok(host_height)
}

Check warning on line 25 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L24-L25

Added lines #L24 - L25 were not covered by tests

fn consensus_state_heights(&self, _client_id: &ClientId) -> Result<Vec<Height>, ContextError> {
let heights = self.get_heights()?;

Check warning on line 28 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L27-L28

Added lines #L27 - L28 were not covered by tests

Ok(heights)
}
fn next_consensus_state(
&self,
client_id: &ClientId,
height: &Height,
) -> Result<Option<Self::ConsensusStateRef>, ContextError> {
let next_height = self.get_adjacent_height(height, HeightTravel::Next)?;

Check warning on line 37 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L30-L37

Added lines #L30 - L37 were not covered by tests

match next_height {
Some(h) => {
let cons_state_path = ClientConsensusStatePath::new(
client_id.clone(),
h.revision_number(),
h.revision_height(),
);
self.consensus_state(&cons_state_path).map(Some)

Check warning on line 46 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L39-L46

Added lines #L39 - L46 were not covered by tests
}
None => Ok(None),

Check warning on line 48 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L48

Added line #L48 was not covered by tests
}
}

Check warning on line 50 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L50

Added line #L50 was not covered by tests

fn prev_consensus_state(
&self,
client_id: &ClientId,
height: &Height,
) -> Result<Option<Self::ConsensusStateRef>, ContextError> {
let prev_height = self.get_adjacent_height(height, HeightTravel::Prev)?;

Check warning on line 57 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L52-L57

Added lines #L52 - L57 were not covered by tests

match prev_height {
Some(prev_height) => {
let cons_state_path = ClientConsensusStatePath::new(
client_id.clone(),
prev_height.revision_number(),
prev_height.revision_height(),
);
self.consensus_state(&cons_state_path).map(Some)

Check warning on line 66 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L59-L66

Added lines #L59 - L66 were not covered by tests
}
None => Ok(None),

Check warning on line 68 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L68

Added line #L68 was not covered by tests
}
}

Check warning on line 70 in ibc-clients/cosmwasm/src/context/custom_ctx.rs

View check run for this annotation

Codecov / codecov/patch

ibc-clients/cosmwasm/src/context/custom_ctx.rs#L70

Added line #L70 was not covered by tests
}
Loading

0 comments on commit 9a24f3c

Please sign in to comment.