From 6e0f289433da8a7fac754b0b98ddc315ec5264e5 Mon Sep 17 00:00:00 2001 From: Farhad Shabani Date: Sat, 20 Apr 2024 09:50:57 -0700 Subject: [PATCH] feat: showcase ibc-client-cw --- Cargo.toml | 9 +- src/bin/schema.rs | 2 +- src/context/ctx.rs | 68 ------- src/context/custom_ctx.rs | 246 -------------------------- src/context/execution_ctx.rs | 106 ----------- src/context/mod.rs | 6 - src/context/validation_ctx.rs | 144 --------------- src/contract.rs | 8 +- src/error.rs | 44 ----- src/handlers.rs | 206 --------------------- src/helpers.rs | 21 --- src/lib.rs | 12 -- src/msg.rs | 324 ---------------------------------- src/response.rs | 70 -------- src/types/client_type.rs | 16 +- src/types/codec.rs | 25 --- src/types/header.rs | 6 +- src/types/mod.rs | 2 - 18 files changed, 15 insertions(+), 1300 deletions(-) delete mode 100644 src/context/ctx.rs delete mode 100644 src/context/custom_ctx.rs delete mode 100644 src/context/execution_ctx.rs delete mode 100644 src/context/mod.rs delete mode 100644 src/context/validation_ctx.rs delete mode 100644 src/error.rs delete mode 100644 src/handlers.rs delete mode 100644 src/helpers.rs delete mode 100644 src/msg.rs delete mode 100644 src/response.rs delete mode 100644 src/types/codec.rs diff --git a/Cargo.toml b/Cargo.toml index 18c4aa0..05cafce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,13 +37,14 @@ optimize = """docker run --rm -v "$(pwd)":/code \ [dependencies] base64 = "0.22.0" -cosmwasm-schema = "2.0.1" -cosmwasm-std = "2.0.1" +cosmwasm-schema = "1.5.2" +cosmwasm-std = "1.5.2" cw-storage-plus = "2.0.0" cw2 = "2.0.0" derive_more = "0.99.17" -ibc-core = { version = "0.51.0", default-features = false, features = ["schema"] } -ibc-clients = { version = "0.51.0", default-features = false, features = ["schema"] } +ibc-core = { git = "https://github.com/cosmos/ibc-rs.git", rev = "80b8084", default-features = false, features = ["schema"] } +ibc-clients = { git = "https://github.com/cosmos/ibc-rs.git", rev = "80b8084", default-features = false, features = ["schema"] } +ibc-client-cw = {git = "https://github.com/cosmos/ibc-rs.git", rev = "80b8084", default-features = false } ibc-proto = { version = "0.42.2", default-features = false } prost = "0.12.3" schemars = "0.8.15" diff --git a/src/bin/schema.rs b/src/bin/schema.rs index 9036968..2924f25 100644 --- a/src/bin/schema.rs +++ b/src/bin/schema.rs @@ -1,6 +1,6 @@ use cosmwasm_schema::write_api; -use rollkit_ibc::msg::{InstantiateMsg, SudoMsg}; +use ibc_client_cw::types::{InstantiateMsg, SudoMsg}; fn main() { write_api! { diff --git a/src/context/ctx.rs b/src/context/ctx.rs deleted file mode 100644 index a8078a8..0000000 --- a/src/context/ctx.rs +++ /dev/null @@ -1,68 +0,0 @@ -use ibc_clients::tendermint::consensus_state::ConsensusState as TendermintConsensusState; -use ibc_core::client::context::ClientExecutionContext; -use ibc_core::client::types::error::ClientError; - -use ibc_core::client::context::ClientValidationContext; -use ibc_core::client::types::Height; -use ibc_core::handler::types::error::ContextError; -use ibc_core::host::types::identifiers::ClientId; -use ibc_core::primitives::Timestamp; - -/// Enables conversion (`TryInto` and `From`) between the consensus state type -/// used by the host and the one specific to the Rollkit light client, which -/// is `TendermintConsensusState`. -pub trait ConsensusStateConverter: - TryInto + From -{ -} - -impl ConsensusStateConverter for C where - C: TryInto + From -{ -} - -/// Client's context required during validation -pub trait ValidationContext: ClientValidationContext -where - Self::ConsensusStateRef: ConsensusStateConverter, -{ - /// Returns the current timestamp of the local chain. - fn host_timestamp(&self) -> Result; - - /// Returns the current height of the local chain. - fn host_height(&self) -> Result; - - /// Returns all the heights at which a consensus state is stored - fn consensus_state_heights(&self, client_id: &ClientId) -> Result, ContextError>; - - /// Search for the lowest consensus state higher than `height`. - fn next_consensus_state( - &self, - client_id: &ClientId, - height: &Height, - ) -> Result, ContextError>; - - /// Search for the highest consensus state lower than `height`. - fn prev_consensus_state( - &self, - client_id: &ClientId, - height: &Height, - ) -> Result, ContextError>; -} - -/// Client's context required during execution. -/// -/// This trait is automatically implemented for all types that implement -/// [`ValidationContext`] and [`ClientExecutionContext`] -pub trait ExecutionContext: ValidationContext + ClientExecutionContext -where - Self::ConsensusStateRef: ConsensusStateConverter, -{ -} - -impl ExecutionContext for T -where - T: ValidationContext + ClientExecutionContext, - T::ConsensusStateRef: ConsensusStateConverter, -{ -} diff --git a/src/context/custom_ctx.rs b/src/context/custom_ctx.rs deleted file mode 100644 index e058dc1..0000000 --- a/src/context/custom_ctx.rs +++ /dev/null @@ -1,246 +0,0 @@ -use core::str::FromStr; - -use cosmwasm_std::{Deps, DepsMut, Env, Order, Storage}; -use ibc_clients::wasm_types::client_state::ClientState as WasmClientState; -use ibc_core::client::context::client_state::ClientStateCommon; -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::{ - iteration_key, ClientStatePath, ClientUpdateHeightPath, ClientUpdateTimePath, - ITERATE_CONSENSUS_STATE_PREFIX, -}; -use ibc_core::primitives::proto::{Any, Protobuf}; - -use crate::types::AnyCodec; -use crate::types::ClientType; -use crate::{parse_height, ContractError, GenesisMetadata, HeightTravel}; -use prost::Message; - -type Checksum = Vec; - -/// Context is a wrapper around the deps and env that gives access to the -/// methods of the ibc-rs Validation and Execution traits. -pub struct Context<'a, C: ClientType<'a>> { - deps: Option>, - deps_mut: Option>, - env: Env, - client_id: ClientId, - checksum: Option, - client_type: core::marker::PhantomData, -} - -impl<'a, C: ClientType<'a>> Context<'a, C> { - pub fn new_ref(deps: Deps<'a>, env: Env) -> Result { - let client_id = ClientId::from_str(env.contract.address.as_str())?; - - Ok(Self { - deps: Some(deps), - deps_mut: None, - env, - client_id, - checksum: None, - client_type: core::marker::PhantomData::, - }) - } - - pub fn new_mut(deps: DepsMut<'a>, env: Env) -> Result { - let client_id = ClientId::from_str(env.contract.address.as_str())?; - - Ok(Self { - deps: None, - deps_mut: Some(deps), - env, - client_id, - checksum: None, - client_type: core::marker::PhantomData::, - }) - } - - pub fn env(&self) -> &Env { - &self.env - } - - pub fn log(&self, msg: &str) -> Option<()> { - self.deps.map(|deps| deps.api.debug(msg)) - } - - pub fn client_id(&self) -> ClientId { - self.client_id.clone() - } - - pub fn set_checksum(&mut self, checksum: Checksum) { - self.checksum = Some(checksum); - } - - pub fn retrieve(&self, key: impl AsRef<[u8]>) -> Result, ClientError> { - let value = self - .storage_ref() - .get(key.as_ref()) - .ok_or(ClientError::Other { - description: "key not found".to_string(), - })?; - - Ok(value) - } - - pub fn insert(&mut self, key: impl AsRef<[u8]>, value: impl AsRef<[u8]>) { - self.storage_mut().set(key.as_ref(), value.as_ref()); - } - - pub fn remove(&mut self, key: impl AsRef<[u8]>) { - self.storage_mut().remove(key.as_ref()); - } - - pub fn get_heights(&self) -> Result, ClientError> { - let iterator = self.storage_ref().range(None, None, Order::Ascending); - - iterator.map(|(_, height)| parse_height(height)).collect() - } - - /// Searches for either the earliest next or latest previous height based on - /// the given height and travel direction. - pub fn get_adjacent_height( - &self, - height: &Height, - travel: HeightTravel, - ) -> Result, ClientError> { - let iteration_key = iteration_key(height.revision_number(), height.revision_height()); - - let mut iterator = match travel { - HeightTravel::Prev => { - self.storage_ref() - .range(None, Some(&iteration_key), Order::Descending) - } - HeightTravel::Next => { - self.storage_ref() - .range(Some(&iteration_key), None, Order::Ascending) - } - }; - - iterator - .next() - .map(|(_, height)| parse_height(height)) - .transpose() - } - - pub fn client_update_time_key(&self, height: &Height) -> Vec { - let client_update_time_path = ClientUpdateTimePath::new( - self.client_id().clone(), - height.revision_number(), - height.revision_height(), - ); - - client_update_time_path.leaf().into_bytes() - } - - pub fn client_update_height_key(&self, height: &Height) -> Vec { - let client_update_height_path = ClientUpdateHeightPath::new( - self.client_id(), - height.revision_number(), - height.revision_height(), - ); - - client_update_height_path.leaf().into_bytes() - } - - pub fn get_metadata(&self) -> Result>, ContractError> { - let mut metadata = Vec::::new(); - - let start_key = ITERATE_CONSENSUS_STATE_PREFIX.to_string().into_bytes(); - - let iterator = self - .storage_ref() - .range(Some(&start_key), None, Order::Ascending); - - for (_, encoded_height) in iterator { - let height = parse_height(encoded_height); - - match height { - Ok(height) => { - let processed_height_key = self.client_update_height_key(&height); - metadata.push(GenesisMetadata { - key: processed_height_key.clone(), - value: self.retrieve(&processed_height_key)?, - }); - let processed_time_key = self.client_update_time_key(&height); - metadata.push(GenesisMetadata { - key: processed_time_key.clone(), - value: self.retrieve(&processed_time_key)?, - }); - } - Err(_) => break, - } - } - - let iterator = self - .storage_ref() - .range(Some(&start_key), None, Order::Ascending); - - for (key, height) in iterator { - metadata.push(GenesisMetadata { key, value: height }); - } - - Ok(Some(metadata)) - } - - pub fn obtain_checksum(&self) -> Result { - match &self.checksum { - Some(checksum) => Ok(checksum.clone()), - None => { - let client_state_value = self.retrieve(ClientStatePath::leaf())?; - - let wasm_client_state: WasmClientState = - Protobuf::::decode(client_state_value.as_slice()).map_err(|e| { - ClientError::Other { - description: e.to_string(), - } - })?; - - Ok(wasm_client_state.checksum) - } - } - } - - pub fn encode_client_state( - &self, - client_state: C::ClientState, - ) -> Result, ClientError> { - let wasm_client_state = WasmClientState { - data: C::ClientState::encode_thru_any(client_state.clone()), - checksum: self.obtain_checksum()?, - latest_height: client_state.latest_height(), - }; - - Ok(Any::from(wasm_client_state).encode_to_vec()) - } -} - -pub trait StorageRef { - fn storage_ref(&self) -> &dyn Storage; -} - -impl<'a, C: ClientType<'a>> StorageRef for Context<'a, C> { - fn storage_ref(&self) -> &dyn Storage { - match self.deps { - Some(ref deps) => deps.storage, - None => match self.deps_mut { - Some(ref deps) => deps.storage, - None => panic!("storage should be available"), - }, - } - } -} - -pub trait StorageMut: StorageRef { - fn storage_mut(&mut self) -> &mut dyn Storage; -} - -impl<'a, C: ClientType<'a>> StorageMut for Context<'a, C> { - fn storage_mut(&mut self) -> &mut dyn Storage { - match self.deps_mut { - Some(ref mut deps) => deps.storage, - None => panic!("storage should be available"), - } - } -} diff --git a/src/context/execution_ctx.rs b/src/context/execution_ctx.rs deleted file mode 100644 index d8c87ba..0000000 --- a/src/context/execution_ctx.rs +++ /dev/null @@ -1,106 +0,0 @@ -use ibc_clients::wasm_types::consensus_state::ConsensusState as WasmConsensusState; -use ibc_core::client::context::ClientExecutionContext; -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::Timestamp; - -use super::Context; -use crate::types::AnyCodec; -use crate::types::ClientType; - -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 key = ClientStatePath::leaf().into_bytes(); - - let encoded_client_state = self.encode_client_state(client_state)?; - - self.insert(key, encoded_client_state); - - Ok(()) - } - - fn store_consensus_state( - &mut self, - consensus_state_path: ClientConsensusStatePath, - consensus_state: Self::ConsensusStateRef, - ) -> Result<(), ContextError> { - let key = consensus_state_path.leaf().into_bytes(); - - 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(key, encoded_wasm_consensus_state); - - Ok(()) - } - - fn delete_consensus_state( - &mut self, - consensus_state_path: ClientConsensusStatePath, - ) -> Result<(), ContextError> { - self.remove(consensus_state_path.leaf().into_bytes()); - - Ok(()) - } - - 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 time_vec: [u8; 8] = host_timestamp.nanoseconds().to_be_bytes(); - - self.insert(time_key, time_vec); - - let height_key = self.client_update_height_key(&height); - - let revision_height_vec: [u8; 8] = host_height.revision_height().to_be_bytes(); - - self.insert(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(()) - } - - fn delete_update_meta( - &mut self, - _client_id: ClientId, - height: Height, - ) -> Result<(), ContextError> { - let time_key = self.client_update_time_key(&height); - - self.remove(time_key); - - let height_key = self.client_update_height_key(&height); - - self.remove(height_key); - - let iteration_key = iteration_key(height.revision_number(), height.revision_height()); - - self.remove(iteration_key); - - Ok(()) - } -} diff --git a/src/context/mod.rs b/src/context/mod.rs deleted file mode 100644 index 5f55695..0000000 --- a/src/context/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -// mod ctx; -mod custom_ctx; -mod execution_ctx; -mod validation_ctx; - -pub use custom_ctx::*; diff --git a/src/context/validation_ctx.rs b/src/context/validation_ctx.rs deleted file mode 100644 index 47a8c7c..0000000 --- a/src/context/validation_ctx.rs +++ /dev/null @@ -1,144 +0,0 @@ -use ibc_clients::tendermint::context::{ConsensusStateConverter, ValidationContext}; -use ibc_clients::wasm_types::client_state::ClientState as WasmClientState; -use ibc_clients::wasm_types::consensus_state::ConsensusState as WasmConsensusState; -use ibc_core::client::context::ClientValidationContext; -use ibc_core::client::types::{error::ClientError, Height}; -use ibc_core::handler::types::error::ContextError; -use ibc_core::host::types::identifiers::ClientId; -use ibc_core::host::types::path::{ClientConsensusStatePath, ClientStatePath}; -use ibc_core::primitives::proto::{Any, Protobuf}; -use ibc_core::primitives::Timestamp; - -use super::Context; -use crate::helpers::HeightTravel; -use crate::types::AnyCodec; -use crate::types::ClientType; - -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 { - let client_state_value = self.retrieve(ClientStatePath::leaf())?; - - let wasm_client_state: WasmClientState = - Protobuf::::decode(client_state_value.as_slice()).map_err(|e| { - ClientError::Other { - description: e.to_string(), - } - })?; - - let tm_client_state = C::ClientState::decode_thru_any(wasm_client_state.data)?; - - Ok(tm_client_state) - } - - fn consensus_state( - &self, - client_cons_state_path: &ClientConsensusStatePath, - ) -> Result { - let consensus_state_value = self.retrieve(client_cons_state_path.leaf())?; - - let wasm_consensus_state: WasmConsensusState = - Protobuf::::decode(consensus_state_value.as_slice()).map_err(|e| { - ClientError::Other { - description: e.to_string(), - } - })?; - - let tm_consensus_state = C::ConsensusState::decode_thru_any(wasm_consensus_state.data)?; - - Ok(tm_consensus_state) - } - - fn client_update_meta( - &self, - _client_id: &ClientId, - height: &Height, - ) -> Result<(Timestamp, Height), ContextError> { - let time_key = self.client_update_time_key(height); - - let time_vec = self.retrieve(time_key)?; - - let time = u64::from_be_bytes(time_vec.try_into().expect("invalid timestamp")); - - let timestamp = - Timestamp::from_nanoseconds(time).map_err(ClientError::InvalidPacketTimestamp)?; - - let height_key = self.client_update_height_key(height); - - let revision_height_vec = self.retrieve(height_key)?; - - let revision_height = - u64::from_be_bytes(revision_height_vec.try_into().expect("invalid height")); - - let height = Height::new(0, revision_height)?; - - Ok((timestamp, height)) - } -} - -impl<'a, C: ClientType<'a>> ValidationContext for Context<'a, C> -where - >::ConsensusState: ConsensusStateConverter, -{ - fn host_timestamp(&self) -> Result { - let time = self.env().block.time; - - let host_timestamp = Timestamp::from_nanoseconds(time.nanos()).expect("invalid timestamp"); - - Ok(host_timestamp) - } - - fn host_height(&self) -> Result { - let host_height = Height::new(0, self.env().block.height)?; - - Ok(host_height) - } - - fn consensus_state_heights(&self, _client_id: &ClientId) -> Result, ContextError> { - let heights = self.get_heights()?; - - Ok(heights) - } - - fn next_consensus_state( - &self, - client_id: &ClientId, - height: &Height, - ) -> Result, ContextError> { - let next_height = self.get_adjacent_height(height, HeightTravel::Next)?; - - 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) - } - None => Ok(None), - } - } - - fn prev_consensus_state( - &self, - client_id: &ClientId, - height: &Height, - ) -> Result, ContextError> { - let prev_height = self.get_adjacent_height(height, HeightTravel::Prev)?; - - 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) - } - None => Ok(None), - } - } -} diff --git a/src/contract.rs b/src/contract.rs index e5515b3..e533e31 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -2,12 +2,10 @@ use cosmwasm_std::entry_point; use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult}; -// use cw2::set_contract_version; - -use crate::context::Context; -use crate::error::ContractError; -use crate::msg::{InstantiateMsg, QueryMsg, SudoMsg}; use crate::types::RollkitClient; +use ibc_client_cw::context::Context; +use ibc_client_cw::types::ContractError; +use ibc_client_cw::types::{InstantiateMsg, QueryMsg, SudoMsg}; pub type RollkitContext<'a> = Context<'a, RollkitClient>; diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 2eac5de..0000000 --- a/src/error.rs +++ /dev/null @@ -1,44 +0,0 @@ -use std::error::Error as StdError; - -use cosmwasm_std::StdError as CwError; -use derive_more::{Display, From}; -use ibc_core::client::types::error::ClientError; -use ibc_core::commitment_types::error::CommitmentError; -use ibc_core::handler::types::error::ContextError; -use ibc_core::host::types::error::IdentifierError; -use ibc_core::host::types::path::PathError; - -use crate::types::Error; - -#[derive(From, Display, Debug)] -pub enum ContractError { - Std(CwError), - #[display(fmt = "invalid message: {_0}")] - InvalidMsg(String), - #[display(fmt = "IBC validation/execution context error: {_0}")] - Context(ContextError), - #[display(fmt = "IBC 02-client error: {_0}")] - Ics02ClientError(ClientError), - #[display(fmt = "IBC commitment error: {_0}")] - Commitment(CommitmentError), - #[display(fmt = "IBC identifier error: {_0}")] - Identifier(IdentifierError), - #[display(fmt = "IBC path error: {_0}")] - Path(PathError), - #[display(fmt = "Proto decode error: {_0}")] - ProtoDecode(prost::DecodeError), -} - -impl StdError for ContractError {} - -impl From for CwError { - fn from(err: ContractError) -> CwError { - CwError::generic_err(err.to_string()) - } -} - -impl From for ContractError { - fn from(err: Error) -> ContractError { - ContractError::Std(CwError::generic_err(err.origin)) - } -} diff --git a/src/handlers.rs b/src/handlers.rs deleted file mode 100644 index 33cf5d1..0000000 --- a/src/handlers.rs +++ /dev/null @@ -1,206 +0,0 @@ -use cosmwasm_std::{to_json_binary, Binary}; -use ibc_core::client::context::prelude::*; -use ibc_core::host::types::path::ClientConsensusStatePath; -use ibc_core::primitives::proto::Any; -use prost::Message; - -use crate::context::Context; -use crate::error::ContractError; -use crate::types::{ClientMessage, ClientType}; -use crate::{ - CheckForMisbehaviourMsg, ContractResult, ExportMetadataMsg, InstantiateMsg, QueryMsg, - QueryResponse, StatusMsg, SudoMsg, UpdateStateMsg, UpdateStateOnMisbehaviourMsg, - VerifyClientMessageMsg, VerifyMembershipMsg, VerifyNonMembershipMsg, - VerifyUpgradeAndUpdateStateMsg, -}; - -impl<'a, C: ClientType<'a>> Context<'a, C> { - pub fn instantiate(&mut self, msg: InstantiateMsg) -> Result { - let any_client_state = Any::decode(&mut msg.client_state.as_slice())?; - - let client_state = C::ClientState::try_from(any_client_state)?; - - let any_consensus_state = Any::decode(&mut msg.consensus_state.as_slice())?; - - self.set_checksum(msg.checksum); - - client_state.initialise(self, &self.client_id(), any_consensus_state)?; - - Ok(to_json_binary(&ContractResult::success())?) - } - - pub fn sudo(&mut self, msg: SudoMsg) -> Result { - let client_id = self.client_id(); - - let client_state = self.client_state(&client_id)?; - - let result = match msg { - SudoMsg::UpdateState(msg_raw) => { - let msg = UpdateStateMsg::try_from(msg_raw)?; - - let any_client_msg = match msg.client_message { - ClientMessage::Header(header) => (*header).into(), - ClientMessage::Misbehaviour(misbehaviour) => (*misbehaviour).into(), - }; - - let heights = client_state.update_state(self, &client_id, any_client_msg)?; - - ContractResult::success().heights(heights) - } - SudoMsg::UpdateStateOnMisbehaviour(msg_raw) => { - let msg: UpdateStateOnMisbehaviourMsg = - UpdateStateOnMisbehaviourMsg::try_from(msg_raw)?; - - let any_client_msg = match msg.client_message { - ClientMessage::Header(header) => (*header).into(), - ClientMessage::Misbehaviour(misbehaviour) => (*misbehaviour).into(), - }; - - client_state.update_state_on_misbehaviour(self, &client_id, any_client_msg)?; - - // TODO: delete consensus state at misbehaviour height - - ContractResult::success() - } - SudoMsg::VerifyMembership(msg) => { - // TODO: check DA light client is active - // TODO: assert(processedTime + clientState.fraudPeriod > currentTimestamp()) - - let msg = VerifyMembershipMsg::try_from(msg)?; - - let client_cons_state_path = ClientConsensusStatePath::new( - self.client_id(), - msg.height.revision_number(), - msg.height.revision_height(), - ); - - let consensus_state = self.consensus_state(&client_cons_state_path)?; - - client_state.verify_membership( - &msg.prefix, - &msg.proof, - consensus_state.root(), - msg.path, - msg.value, - )?; - - ContractResult::success() - } - SudoMsg::VerifyNonMembership(msg) => { - // TODO: check DA light client is active - // TODO: assert(processedTime + clientState.fraudPeriod > currentTimestamp()) - - let msg = VerifyNonMembershipMsg::try_from(msg)?; - - let client_cons_state_path = ClientConsensusStatePath::new( - client_id.clone(), - msg.height.revision_number(), - msg.height.revision_height(), - ); - - let consensus_state = self.consensus_state(&client_cons_state_path)?; - - client_state.verify_non_membership( - &msg.prefix, - &msg.proof, - consensus_state.root(), - msg.path, - )?; - - ContractResult::success() - } - SudoMsg::VerifyUpgradeAndUpdateState(msg) => { - let msg = VerifyUpgradeAndUpdateStateMsg::try_from(msg)?; - - let client_cons_state_path = ClientConsensusStatePath::new( - client_id.clone(), - client_state.latest_height().revision_number(), - client_state.latest_height().revision_height(), - ); - - let consensus_state = self.consensus_state(&client_cons_state_path)?; - - client_state.verify_upgrade_client( - msg.upgrade_client_state.clone(), - msg.upgrade_consensus_state.clone(), - msg.proof_upgrade_client, - msg.proof_upgrade_consensus_state, - consensus_state.root(), - )?; - - client_state.update_state_on_upgrade( - self, - &client_id, - msg.upgrade_client_state, - msg.upgrade_consensus_state, - )?; - - ContractResult::success() - } - SudoMsg::MigrateClientStore(_) => { - return Err(ContractError::InvalidMsg( - "ibc-rs does no support this feature yet".to_string(), - )); - } - }; - Ok(to_json_binary(&result)?) - } - - pub fn query(&self, msg: QueryMsg) -> Result { - let client_id = self.client_id(); - - let client_state = self.client_state(&client_id)?; - - let resp = match msg { - QueryMsg::Status(StatusMsg {}) => match client_state.status(self, &client_id) { - Ok(status) => QueryResponse::success().status(status.to_string()), - Err(err) => QueryResponse::success().status(err.to_string()), - }, - QueryMsg::ExportMetadata(ExportMetadataMsg {}) => { - QueryResponse::success().genesis_metadata(self.get_metadata()?) - } - QueryMsg::TimestampAtHeight(msg) => { - let client_cons_state_path = ClientConsensusStatePath::new( - client_id, - msg.height.revision_number(), - msg.height.revision_height(), - ); - - let consensus_state = self.consensus_state(&client_cons_state_path)?; - - QueryResponse::success().timestamp(consensus_state.timestamp().nanoseconds()) - } - QueryMsg::VerifyClientMessage(msg) => { - let msg = VerifyClientMessageMsg::try_from(msg)?; - - let any_client_msg: Any = match msg.client_message { - ClientMessage::Header(header) => (*header).into(), - ClientMessage::Misbehaviour(misbehaviour) => (*misbehaviour).into(), - }; - - client_state.verify_client_message(self, &client_id, any_client_msg)?; - - // TODO: in case client message is a header, verify header proof and block data proof on the DA light client - - QueryResponse::success() - } - QueryMsg::CheckForMisbehaviour(msg) => { - let msg = CheckForMisbehaviourMsg::try_from(msg)?; - - let any_client_msg: Any = match msg.client_message { - ClientMessage::Header(header) => (*header).into(), - ClientMessage::Misbehaviour(misbehaviour) => (*misbehaviour).into(), - }; - - let result = - client_state.check_for_misbehaviour(self, &client_id, any_client_msg)?; - - // TODO: handle fraud proofs - - QueryResponse::success().misbehaviour(result) - } - }; - - Ok(to_json_binary(&resp)?) - } -} diff --git a/src/helpers.rs b/src/helpers.rs deleted file mode 100644 index 3f19354..0000000 --- a/src/helpers.rs +++ /dev/null @@ -1,21 +0,0 @@ -use ibc_core::client::types::error::ClientError; -use ibc_core::client::types::Height; - -/// Travel is an enum to represent the direction of travel in the context of -/// height. -pub enum HeightTravel { - Next, - Prev, -} - -/// Decodes a `Height` from a UTF-8 encoded byte array. -pub fn parse_height(encoded_height: Vec) -> Result { - let height_str = - core::str::from_utf8(encoded_height.as_slice()).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - - Height::try_from(height_str).map_err(|e| ClientError::Other { - description: e.to_string(), - }) -} diff --git a/src/lib.rs b/src/lib.rs index 216e86b..7249c73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,17 +9,5 @@ )] #![forbid(unsafe_code)] -pub mod context; pub mod contract; -mod error; -pub mod handlers; -pub mod helpers; -pub mod msg; -pub mod response; pub mod types; - -pub use crate::error::ContractError; -pub use crate::helpers::*; -pub use crate::msg::*; -pub use crate::response::GenesisMetadata; -pub use crate::response::*; diff --git a/src/msg.rs b/src/msg.rs deleted file mode 100644 index 0f57379..0000000 --- a/src/msg.rs +++ /dev/null @@ -1,324 +0,0 @@ -//! Contains the definition of the messages that can be sent to the CosmWasm contract. - -use core::str::FromStr; - -use base64::prelude::BASE64_STANDARD; -use base64::Engine; -use cosmwasm_schema::cw_serde; -use serde::de::Error; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -use ibc_core::client::types::error::ClientError; -use ibc_core::client::types::proto::v1::Height as RawHeight; -use ibc_core::client::types::Height; -use ibc_core::commitment_types::commitment::{CommitmentPrefix, CommitmentProofBytes}; -use ibc_core::handler::types::error::ContextError; -use ibc_core::host::types::path::Path; -use ibc_core::primitives::proto::Any; -use prost::Message; - -use crate::error::ContractError; -use crate::types::ClientMessage; - -pub type Bytes = Vec; - -// IDEA: this could be removed if we import ibc_client_wasm_types::serializer::Base64; -// but we need to import a separate crate for that, because we need the "cosmwasm" feature -pub struct Base64; - -impl Base64 { - pub fn serialize(bytes: &[u8], serializer: S) -> Result { - let encoded = BASE64_STANDARD.encode(bytes); - String::serialize(&encoded, serializer) - } - - pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { - let base64 = String::deserialize(deserializer)?; - let bytes = BASE64_STANDARD - .decode(base64.as_bytes()) - .map_err(Error::custom)?; - - Ok(bytes) - } -} - -// ------------------------------------------------------------ -// Implementation of the InstantiateMsg struct -// ------------------------------------------------------------ - -#[cw_serde] -pub struct InstantiateMsg { - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub client_state: Bytes, - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub consensus_state: Bytes, - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub checksum: Bytes, -} - -// ------------------------------------------------------------ -// Implementation of the SudoMsg enum and its variants -// ------------------------------------------------------------ - -#[cw_serde] -pub enum SudoMsg { - UpdateState(UpdateStateMsgRaw), - UpdateStateOnMisbehaviour(UpdateStateOnMisbehaviourMsgRaw), - VerifyUpgradeAndUpdateState(VerifyUpgradeAndUpdateStateMsgRaw), - VerifyMembership(VerifyMembershipMsgRaw), - VerifyNonMembership(VerifyNonMembershipMsgRaw), - MigrateClientStore(MigrateClientStoreMsg), -} - -#[cw_serde] -pub struct UpdateStateMsgRaw { - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub client_message: Bytes, -} - -pub struct UpdateStateMsg { - pub client_message: ClientMessage, -} - -impl TryFrom for UpdateStateMsg { - type Error = ContractError; - - fn try_from(raw: UpdateStateMsgRaw) -> Result { - let client_message = ClientMessage::decode(raw.client_message)?; - Ok(Self { client_message }) - } -} - -#[cw_serde] -pub struct UpdateStateOnMisbehaviourMsgRaw { - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub client_message: Bytes, -} - -pub struct UpdateStateOnMisbehaviourMsg { - pub client_message: ClientMessage, -} - -impl TryFrom for UpdateStateOnMisbehaviourMsg { - type Error = ContractError; - - fn try_from(raw: UpdateStateOnMisbehaviourMsgRaw) -> Result { - let client_message = ClientMessage::decode(raw.client_message)?; - - Ok(Self { client_message }) - } -} - -#[cw_serde] -pub struct CheckSubstituteAndUpdateStateMsg {} - -#[cw_serde] -pub struct VerifyUpgradeAndUpdateStateMsgRaw { - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub upgrade_client_state: Bytes, - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub upgrade_consensus_state: Bytes, - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub proof_upgrade_client: Bytes, - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub proof_upgrade_consensus_state: Bytes, -} - -pub struct VerifyUpgradeAndUpdateStateMsg { - pub upgrade_client_state: Any, - pub upgrade_consensus_state: Any, - pub proof_upgrade_client: CommitmentProofBytes, - pub proof_upgrade_consensus_state: CommitmentProofBytes, -} - -impl TryFrom for VerifyUpgradeAndUpdateStateMsg { - type Error = ContractError; - - fn try_from(raw: VerifyUpgradeAndUpdateStateMsgRaw) -> Result { - let upgrade_client_state = Any::decode(&mut raw.upgrade_client_state.as_slice())?; - - let upgrade_consensus_state = Any::decode(&mut raw.upgrade_consensus_state.as_slice())?; - - Ok(VerifyUpgradeAndUpdateStateMsg { - upgrade_client_state, - upgrade_consensus_state, - proof_upgrade_client: CommitmentProofBytes::try_from(raw.proof_upgrade_client)?, - proof_upgrade_consensus_state: CommitmentProofBytes::try_from( - raw.proof_upgrade_consensus_state, - )?, - }) - } -} - -#[cw_serde] -pub struct MerklePath { - pub key_path: Vec, -} - -#[cw_serde] -pub struct VerifyMembershipMsgRaw { - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub proof: Bytes, - pub path: MerklePath, - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub value: Bytes, - pub height: RawHeight, - pub delay_block_period: u64, - pub delay_time_period: u64, -} - -pub struct VerifyMembershipMsg { - pub prefix: CommitmentPrefix, - pub proof: CommitmentProofBytes, - pub path: Path, - pub value: Vec, - pub height: Height, - pub delay_block_period: u64, - pub delay_time_period: u64, -} - -impl TryFrom for VerifyMembershipMsg { - type Error = ContractError; - - fn try_from(mut raw: VerifyMembershipMsgRaw) -> Result { - let proof = CommitmentProofBytes::try_from(raw.proof)?; - let prefix = raw.path.key_path.remove(0).into_bytes(); - let path_str = raw.path.key_path.join(""); - let path = Path::from_str(&path_str)?; - let height = Height::try_from(raw.height).map_err(|e| { - ContractError::Context(ContextError::ClientError(ClientError::Other { - description: e.to_string(), - })) - })?; - Ok(Self { - proof, - path, - value: raw.value, - height, - prefix: CommitmentPrefix::try_from(prefix)?, - delay_block_period: raw.delay_block_period, - delay_time_period: raw.delay_time_period, - }) - } -} - -#[cw_serde] -pub struct VerifyNonMembershipMsgRaw { - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub proof: Bytes, - pub path: MerklePath, - pub height: RawHeight, - pub delay_block_period: u64, - pub delay_time_period: u64, -} - -pub struct VerifyNonMembershipMsg { - pub prefix: CommitmentPrefix, - pub proof: CommitmentProofBytes, - pub path: Path, - pub height: Height, - pub delay_block_period: u64, - pub delay_time_period: u64, -} - -impl TryFrom for VerifyNonMembershipMsg { - type Error = ContractError; - - fn try_from(mut raw: VerifyNonMembershipMsgRaw) -> Result { - let proof = CommitmentProofBytes::try_from(raw.proof)?; - let prefix = raw.path.key_path.remove(0).into_bytes(); - let path_str = raw.path.key_path.join(""); - let path = Path::from_str(&path_str)?; - let height = raw.height.try_into().expect("invalid height"); - Ok(Self { - proof, - path, - height, - prefix: CommitmentPrefix::try_from(prefix)?, - delay_block_period: raw.delay_block_period, - delay_time_period: raw.delay_time_period, - }) - } -} - -#[cw_serde] -pub struct MigrateClientStoreMsg {} - -// ------------------------------------------------------------ -// Implementation of the QueryMsg enum and its variants -// ------------------------------------------------------------ - -#[cw_serde] -// #[derive(QueryResponses)] TODO: we might want to add this macro, but it didn't for now because it required some extra work and it's not strictly necessary. -pub enum QueryMsg { - Status(StatusMsg), - ExportMetadata(ExportMetadataMsg), - TimestampAtHeight(TimestampAtHeightMsg), - VerifyClientMessage(VerifyClientMessageRaw), - CheckForMisbehaviour(CheckForMisbehaviourMsgRaw), -} - -#[cw_serde] -pub struct StatusMsg {} - -#[cw_serde] -pub struct ExportMetadataMsg {} - -#[cw_serde] -pub struct TimestampAtHeightMsg { - pub height: Height, -} - -#[cw_serde] -pub struct VerifyClientMessageRaw { - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub client_message: Bytes, -} - -pub struct VerifyClientMessageMsg { - pub client_message: ClientMessage, -} - -impl TryFrom for VerifyClientMessageMsg { - type Error = ContractError; - - fn try_from(raw: VerifyClientMessageRaw) -> Result { - let client_message = ClientMessage::decode(raw.client_message)?; - - Ok(Self { client_message }) - } -} - -#[cw_serde] -pub struct CheckForMisbehaviourMsgRaw { - #[schemars(with = "String")] - #[serde(with = "Base64", default)] - pub client_message: Bytes, -} - -pub struct CheckForMisbehaviourMsg { - pub client_message: ClientMessage, -} - -impl TryFrom for CheckForMisbehaviourMsg { - type Error = ContractError; - - fn try_from(raw: CheckForMisbehaviourMsgRaw) -> Result { - let client_message = ClientMessage::decode(raw.client_message)?; - - Ok(Self { client_message }) - } -} diff --git a/src/response.rs b/src/response.rs deleted file mode 100644 index 6b814cd..0000000 --- a/src/response.rs +++ /dev/null @@ -1,70 +0,0 @@ -use cosmwasm_schema::cw_serde; -use ibc_core::client::types::Height; - -#[cw_serde] -pub struct GenesisMetadata { - pub key: Vec, - pub value: Vec, -} - -#[cw_serde] -pub struct QueryResponse { - pub is_valid: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub status: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub genesis_metadata: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub found_misbehaviour: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub timestamp: Option, -} - -impl QueryResponse { - pub fn success() -> Self { - Self { - is_valid: true, - status: None, - genesis_metadata: None, - found_misbehaviour: None, - timestamp: None, - } - } - - pub fn status(mut self, status: String) -> Self { - self.status = Some(status); - self - } - - pub fn genesis_metadata(mut self, genesis_metadata: Option>) -> Self { - self.genesis_metadata = genesis_metadata; - self - } - - pub fn misbehaviour(mut self, found_misbehavior: bool) -> Self { - self.found_misbehaviour = Some(found_misbehavior); - self - } - - pub fn timestamp(mut self, timestamp: u64) -> Self { - self.timestamp = Some(timestamp); - self - } -} - -#[cw_serde] -pub struct ContractResult { - #[serde(skip_serializing_if = "Option::is_none")] - pub heights: Option>, -} - -impl ContractResult { - pub fn success() -> Self { - Self { heights: None } - } - - pub fn heights(mut self, heights: Vec) -> Self { - self.heights = Some(heights); - self - } -} diff --git a/src/types/client_type.rs b/src/types/client_type.rs index 2dbeb57..0655b0e 100644 --- a/src/types/client_type.rs +++ b/src/types/client_type.rs @@ -1,11 +1,6 @@ -use ibc_clients::tendermint::client_state::ClientState as TendermintClientState; -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; use crate::types::AnyConsensusState; +use ibc_client_cw::api::ClientType; +use ibc_clients::tendermint::client_state::ClientState as TendermintClientState; pub struct RollkitClient; @@ -13,10 +8,3 @@ impl<'a> ClientType<'a> for RollkitClient { type ClientState = TendermintClientState; type ConsensusState = AnyConsensusState; } - -/// Enables the introduction of custom client and consensus state types tailored -/// for Rollkit light clients. -pub trait ClientType<'a>: Sized { - type ClientState: ClientStateExecution> + Clone; - type ConsensusState: ConsensusStateTrait + Into + TryFrom; -} diff --git a/src/types/codec.rs b/src/types/codec.rs deleted file mode 100644 index 20faf8c..0000000 --- a/src/types/codec.rs +++ /dev/null @@ -1,25 +0,0 @@ -use ibc_core::client::types::error::ClientError; -use ibc_core::primitives::proto::Any; -use prost::Message; - -pub trait AnyCodec { - fn decode_thru_any(data: Vec) -> Result - where - C: TryFrom, - { - let raw = Any::decode(&mut data.as_slice()).map_err(|e| ClientError::Other { - description: e.to_string(), - })?; - - C::try_from(raw) - } - - fn encode_thru_any(value: C) -> Vec - where - C: Into, - { - value.into().encode_to_vec() - } -} - -impl AnyCodec for T where T: TryFrom + Into {} diff --git a/src/types/header.rs b/src/types/header.rs index 61119de..20feea6 100644 --- a/src/types/header.rs +++ b/src/types/header.rs @@ -7,6 +7,8 @@ use ibc_core::client::types::Height; use ibc_core::primitives::proto::{Any, Protobuf}; use ibc_core::primitives::Timestamp; use ibc_proto::ibc::lightclients::rollkit::v1::Header as RawRollkitHeader; +use tendermint::crypto::Sha256; +use tendermint::merkle::MerkleHash; use crate::types::DaData; use crate::types::Error; @@ -46,9 +48,9 @@ impl Header { } /// Checks if the fields of a given header are consistent with the trusted fields of this header. - pub fn validate_basic(&self) -> Result<(), Error> { + pub fn validate_basic(&self) -> Result<(), Error> { self.tendermint_header - .validate_basic() + .validate_basic::() .map_err(Error::source) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 21456b1..acfcc91 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,6 +1,5 @@ mod client_message; mod client_type; -mod codec; mod consensus_state; mod da_data; mod error; @@ -8,7 +7,6 @@ mod header; pub use client_message::*; pub use client_type::*; -pub use codec::*; pub use consensus_state::*; pub use da_data::*; pub use error::*;