Skip to content

Commit

Permalink
cf-guest-cw: add light client implementation for the guest blockchain (
Browse files Browse the repository at this point in the history
…#478)



---------

Signed-off-by: Michal Nazarewicz <mina86@mina86.com>
Co-authored-by: Dhruv D Jain <dhruv@iamsizzling.com>
  • Loading branch information
mina86 and dhruvja authored Mar 11, 2024
1 parent 7ae8b4f commit 46588f5
Show file tree
Hide file tree
Showing 13 changed files with 2,241 additions and 195 deletions.
1,143 changes: 949 additions & 194 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ members = [

# ibc light clients
"light-clients/common",
"light-clients/cf-guest-cw",
"light-clients/ics07-tendermint",
"light-clients/ics07-tendermint-cw",
"light-clients/ics08-wasm",
Expand Down Expand Up @@ -228,4 +229,4 @@ substrate-state-trie-migration-rpc = { git = "https://github.com/paritytech//sub
substrate-wasm-builder = { git = "https://github.com/paritytech//substrate.git", branch = "polkadot-v0.9.43" }

[profile.release]
overflow-checks = true
overflow-checks = true
57 changes: 57 additions & 0 deletions light-clients/cf-guest-cw/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[package]
name = "cf-guest-cw"
version = "0.0.0"
authors = ["Michal Nazarewicz <mina86@mina86.com>"]
edition = "2021"

exclude = [
# Those files are rust-optimizer artifacts. You might want to
# commit them for convenience but they should not be part of the
# source code publication.
"contract.wasm",
"hash.txt",
]

[lib]
crate-type = ["cdylib", "rlib"]

[package.metadata.scripts]
optimize = """docker run --rm -v "$(pwd)":/code \
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
cosmwasm/rust-optimizer:0.12.6
"""

[dependencies]
cf-guest = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false }
guestchain = { git = "https://github.com/ComposableFi/emulated-light-client/", default-features = false }

base64 = { version = "0.22", default-features = false, features = ["alloc"] }
borsh = { version = "0.10.3", default-features = false }
cosmwasm-schema = "1.5.3"
cosmwasm-std = "1.5.3"
derive_more = "0.99.17"
ed25519-dalek = { version = "2.1.1", default-features = false, features = ["pkcs8"] }

# Same version emulated-light-client uses
ibc = { git = "https://github.com/cosmos/ibc-rs", rev = "6dd3c6465e594d4c177f21724dd896a15e8f3634", default-features = false, features = ["borsh", "serde"] }

prost = { version = "0.12.3", default-features = false }
schemars = "0.8.10"
serde = { version = "1.0.145", default-features = false, features = ["derive"] }
sha2 = "0.10"

[features]
# for more explicit tests, cargo test --features=backtraces
backtraces = ["cosmwasm-std/backtraces"]
# use library feature to disable all instantiate/execute/query exports
library = []
default = []
std = [
"base64/std",
"ed25519-dalek/std",
"ibc/std",
"prost/std",
"serde/std",
"sha2/std",
]
25 changes: 25 additions & 0 deletions light-clients/cf-guest-cw/src/bin/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2022 ComposableFi.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use cf_guest_cw::msg;
use cosmwasm_schema::write_api;

fn main() {
write_api! {
instantiate: msg::InstantiateMsg,
sudo: msg::SudoMsg,
query: msg::QueryMsg,
}
}
182 changes: 182 additions & 0 deletions light-clients/cf-guest-cw/src/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright (C) 2022 ComposableFi.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use core::str::FromStr;

use cosmwasm_std::{Api, Deps, DepsMut, Env, Storage};

use crate::{ibc, state};

type Result<T, E = crate::Error> = core::result::Result<T, E>;

/// Base context for handling CosmWasm operations.
///
/// It wraps together access to CosmWasm API and information about the request
/// such as block height, current time and IBC client id corresponding to this
/// contract.
///
/// The object dereferences into [`state::Metadata`] such that metadata fields
/// are directly accessible through this object.
#[derive(derive_more::Deref)]
pub(crate) struct ContextBase<'a> {
#[deref]
pub metadata: state::Metadata,
pub client_id: ibc::ClientId,
pub api: &'a dyn Api,
}

/// Mutable execution context for handling CosmWasm operations.
///
/// It wraps together access to CosmWasm APIs, storage and information about the
/// request. To construct a new context object use [`new`] function.
#[derive(derive_more::Deref)]
pub(crate) struct ContextMut<'a> {
#[deref]
base: ContextBase<'a>,
storage: &'a mut dyn Storage,
}

/// Constructs a new mutable execution context.
pub(crate) fn new<'a>(deps: DepsMut<'a>, env: Env) -> ContextMut<'a> {
ContextMut { base: ContextBase::new(env, deps.api), storage: deps.storage }
}

/// Read-only execution context for handling CosmWasm operations.
///
/// It wraps together access to CosmWasm APIs, storage and information about the
/// request. To construct a new context object use [`new_ro`] function.
///
/// The object dereferences into [`ContextBase`] which holds data common between
/// read-only and mutable execution contexts.
#[derive(derive_more::Deref)]
pub(crate) struct Context<'a> {
#[deref]
base: ContextBase<'a>,
storage: &'a dyn Storage,
}

/// Constructs a new read-only execution context.
pub(crate) fn new_ro<'a>(deps: Deps<'a>, env: Env) -> Context<'a> {
Context { base: ContextBase::new(env, deps.api), storage: deps.storage }
}

impl<'a> ContextBase<'a> {
fn new(env: Env, api: &'a dyn Api) -> Self {
let metadata = state::Metadata {
host_timestamp_ns: env.block.time.nanos(),
host_height: env.block.height,
};
let address = env.contract.address.as_str();
let client_id = ibc::ClientId::from_str(address).unwrap();
Self { client_id, metadata, api }
}

pub fn log(&self, msg: impl alloc::string::ToString) {
self.api.debug(&msg.to_string())
}
}

/// Logs formatted text using CosmWasm API.
///
/// To log string literals prefer [`ContextBase::log`] method.
macro_rules! log {
($self:expr, $($tt:tt)*) => {
$self.log(format_args!($($tt)*))
};
}

pub(crate) use log;

impl<'a> Context<'a> {
/// Reads this light client’s client state from storage.
pub fn client_state(&self) -> Result<state::ClientState> {
req_client_state(&self.client_id, self.client_states().get())
}

/// Returns object providing access to read client state from the
/// storage.
pub fn client_states(&self) -> &'a state::ClientStates {
state::ClientStates::new_ro(self.storage)
}

/// Reads this light client’s consensus state at given height from
/// storage.
pub fn consensus_state(&self, height: ibc::Height) -> Result<state::ConsensusState> {
req_consensus_state(&self.client_id, height, self.consensus_states().get(height))
}

/// Returns object providing access to read consensus states from the
/// storage.
pub fn consensus_states(&self) -> &'a state::ConsensusStates {
state::ConsensusStates::new_ro(self.storage)
}
}

impl<'a> ContextMut<'a> {
/// Reads this light client’s client state from storage.
pub fn client_state(&self) -> Result<state::ClientState> {
req_client_state(&self.client_id, self.client_states().get())
}

/// Returns object providing access to read client state from the
/// storage.
pub fn client_states(&self) -> &state::ClientStates {
state::ClientStates::new_ro(self.storage)
}

/// Returns object providing access to read or write client state
/// from/to the storage.
pub fn client_states_mut(&mut self) -> &mut state::ClientStates {
state::ClientStates::new(self.storage)
}

/// Reads this light client’s consensus state at given height from
/// storage.
pub fn consensus_state(&self, height: ibc::Height) -> Result<state::ConsensusState> {
req_consensus_state(&self.client_id, height, self.consensus_states().get(height))
}

/// Returns object providing access to read consensus states from the
/// storage.
pub fn consensus_states(&self) -> &state::ConsensusStates {
state::ConsensusStates::new_ro(self.storage)
}

/// Returns object providing access to read or write consensus states
/// from/to the storage.
pub fn consensus_states_mut(&mut self) -> &mut state::ConsensusStates {
state::ConsensusStates::new(self.storage)
}
}

/// Returns an error if client state is not present.
fn req_client_state(
client_id: &ibc::ClientId,
state: Result<Option<state::ClientState>>,
) -> Result<state::ClientState> {
let make_err = || ibc::ClientError::ClientStateNotFound { client_id: client_id.clone() }.into();
state.and_then(|state| state.ok_or_else(make_err))
}

/// Returns an error if consensus state is not present.
fn req_consensus_state(
client_id: &ibc::ClientId,
height: ibc::Height,
state: Result<Option<(state::ConsensusState, state::Metadata)>>,
) -> Result<state::ConsensusState> {
let make_err =
|| ibc::ClientError::ConsensusStateNotFound { client_id: client_id.clone(), height }.into();
state.and_then(|state| state.map(|(state, _metadata)| state).ok_or_else(make_err))
}
Loading

0 comments on commit 46588f5

Please sign in to comment.