diff --git a/.gitignore b/.gitignore
index 3dc05158..e91ae694 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
.DS_Store
corelib/
target/
+**/Cargo.lock
# vscode
-.vscode/
\ No newline at end of file
+.vscode/
diff --git a/Cargo.lock b/Cargo.lock
index 40621740..b54ebf25 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -811,6 +811,24 @@ dependencies = [
"serde",
]
+[[package]]
+name = "ibc-client-dummy"
+version = "0.1.0"
+dependencies = [
+ "derive_more 0.99.18",
+ "ibc-core",
+]
+
+[[package]]
+name = "ibc-client-dummy-cw"
+version = "0.1.0"
+dependencies = [
+ "cosmwasm-std",
+ "ibc-client-cw",
+ "ibc-client-dummy",
+ "ibc-core",
+]
+
[[package]]
name = "ibc-client-starknet"
version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 4b4a1d8a..892394ad 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,6 +2,7 @@
resolver = "2"
members = [
"light-client/*",
+ "dummy-light-client/*",
]
[workspace.package]
diff --git a/dummy-light-client/README.md b/dummy-light-client/README.md
new file mode 100644
index 00000000..d69bb972
--- /dev/null
+++ b/dummy-light-client/README.md
@@ -0,0 +1,9 @@
+
+
Dummy Light Client
+
+
+A light client without any validation. Its purpose is to bootstrap IBC without
+having a full light client implementation.
+
+> [!WARNING]\
+> This light client is not secure and should not be used in production.
diff --git a/dummy-light-client/cw-contract/Cargo.toml b/dummy-light-client/cw-contract/Cargo.toml
new file mode 100644
index 00000000..a9b105b3
--- /dev/null
+++ b/dummy-light-client/cw-contract/Cargo.toml
@@ -0,0 +1,30 @@
+[package]
+name = "ibc-client-dummy-cw"
+authors = { workspace = true }
+edition = { workspace = true }
+license = { workspace = true }
+repository = { workspace = true }
+rust-version = { workspace = true }
+version = { workspace = true }
+readme = "./../README.md"
+keywords = [ "dummy", "ibc", "light-client", "CosmWasm" ]
+description = "CosmWasm contract for dummy IBC client."
+
+[lib]
+crate-type = [ "cdylib", "rlib" ]
+
+[dependencies]
+# ibc dependencies
+ibc-core = { workspace = true }
+ibc-client-cw = { workspace = true }
+ibc-client-dummy = { path = "./../impls" }
+
+# cosmwasm dependencies
+cosmwasm-std = "2.1.0"
+
+[features]
+default = [ "std" ]
+std = [
+ "ibc-core/std",
+ "ibc-client-cw/std",
+]
diff --git a/dummy-light-client/cw-contract/src/client_type.rs b/dummy-light-client/cw-contract/src/client_type.rs
new file mode 100644
index 00000000..e4742476
--- /dev/null
+++ b/dummy-light-client/cw-contract/src/client_type.rs
@@ -0,0 +1,9 @@
+use ibc_client_cw::api::ClientType;
+use ibc_client_dummy::{ClientState, ConsensusState};
+
+pub struct DummyLightClient;
+
+impl<'a> ClientType<'a> for DummyLightClient {
+ type ClientState = ClientState;
+ type ConsensusState = ConsensusState;
+}
diff --git a/dummy-light-client/cw-contract/src/entrypoint.rs b/dummy-light-client/cw-contract/src/entrypoint.rs
new file mode 100644
index 00000000..06db2753
--- /dev/null
+++ b/dummy-light-client/cw-contract/src/entrypoint.rs
@@ -0,0 +1,37 @@
+use cosmwasm_std::{entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response};
+use ibc_client_cw::context::Context;
+use ibc_client_cw::types::{ContractError, InstantiateMsg, QueryMsg, SudoMsg};
+
+use crate::client_type::DummyLightClient;
+
+pub type DummyLightClientContext<'a> = Context<'a, DummyLightClient>;
+
+#[entry_point]
+pub fn instantiate(
+ deps: DepsMut<'_>,
+ env: Env,
+ _info: MessageInfo,
+ msg: InstantiateMsg,
+) -> Result {
+ let mut ctx = DummyLightClientContext::new_mut(deps, env)?;
+
+ let data = ctx.instantiate(msg)?;
+
+ Ok(Response::default().set_data(data))
+}
+
+#[entry_point]
+pub fn sudo(deps: DepsMut<'_>, env: Env, msg: SudoMsg) -> Result {
+ let mut ctx = DummyLightClientContext::new_mut(deps, env)?;
+
+ let data = ctx.sudo(msg)?;
+
+ Ok(Response::default().set_data(data))
+}
+
+#[entry_point]
+pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result {
+ let ctx = DummyLightClientContext::new_ref(deps, env)?;
+
+ ctx.query(msg)
+}
diff --git a/dummy-light-client/cw-contract/src/lib.rs b/dummy-light-client/cw-contract/src/lib.rs
new file mode 100644
index 00000000..54fb5442
--- /dev/null
+++ b/dummy-light-client/cw-contract/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod client_type;
+pub mod entrypoint;
diff --git a/dummy-light-client/impls/Cargo.toml b/dummy-light-client/impls/Cargo.toml
new file mode 100644
index 00000000..1c4df4db
--- /dev/null
+++ b/dummy-light-client/impls/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "ibc-client-dummy"
+authors = { workspace = true }
+edition = { workspace = true }
+license = { workspace = true }
+rust-version = { workspace = true }
+version = { workspace = true }
+repository = { workspace = true }
+readme = "./../README.md"
+keywords = [ "dummy", "test", "ibc", "light-client" ]
+description = "A light client without any validation. Only for testing."
+
+[lints]
+workspace = true
+
+[dependencies]
+# external dependencies
+derive_more = { workspace = true }
+
+# ibc dependencies
+ibc-core = { workspace = true }
+
+[features]
+default = [ "std" ]
+std = [
+ "ibc-core/std",
+]
diff --git a/dummy-light-client/impls/src/client_state/common.rs b/dummy-light-client/impls/src/client_state/common.rs
new file mode 100644
index 00000000..601a96c2
--- /dev/null
+++ b/dummy-light-client/impls/src/client_state/common.rs
@@ -0,0 +1,65 @@
+use ibc_core::client::context::client_state::ClientStateCommon;
+use ibc_core::client::types::error::ClientError;
+use ibc_core::client::types::Height;
+use ibc_core::commitment_types::commitment::{
+ CommitmentPrefix, CommitmentProofBytes, CommitmentRoot,
+};
+use ibc_core::host::types::identifiers::ClientType;
+use ibc_core::host::types::path::{Path, PathBytes};
+use ibc_core::primitives::proto::Any;
+
+use super::ClientState;
+
+impl ClientStateCommon for ClientState {
+ fn verify_consensus_state(&self, consensus_state: Any) -> Result<(), ClientError> {
+ Ok(())
+ }
+
+ fn client_type(&self) -> ClientType {
+ "blind-001".parse().unwrap()
+ }
+
+ fn latest_height(&self) -> Height {
+ self.latest_height
+ }
+
+ fn validate_proof_height(&self, proof_height: Height) -> Result<(), ClientError> {
+ Ok(())
+ }
+
+ fn verify_upgrade_client(
+ &self,
+ upgraded_client_state: Any,
+ upgraded_consensus_state: Any,
+ proof_upgrade_client: CommitmentProofBytes,
+ proof_upgrade_consensus_state: CommitmentProofBytes,
+ root: &CommitmentRoot,
+ ) -> Result<(), ClientError> {
+ Ok(())
+ }
+
+ fn serialize_path(&self, path: Path) -> Result {
+ Ok(path.to_string().as_bytes().to_vec().into())
+ }
+
+ fn verify_membership_raw(
+ &self,
+ prefix: &CommitmentPrefix,
+ proof: &CommitmentProofBytes,
+ root: &CommitmentRoot,
+ path: PathBytes,
+ value: Vec,
+ ) -> Result<(), ClientError> {
+ Ok(())
+ }
+
+ fn verify_non_membership_raw(
+ &self,
+ prefix: &CommitmentPrefix,
+ proof: &CommitmentProofBytes,
+ root: &CommitmentRoot,
+ path: PathBytes,
+ ) -> Result<(), ClientError> {
+ Ok(())
+ }
+}
diff --git a/dummy-light-client/impls/src/client_state/execution.rs b/dummy-light-client/impls/src/client_state/execution.rs
new file mode 100644
index 00000000..47ab0af9
--- /dev/null
+++ b/dummy-light-client/impls/src/client_state/execution.rs
@@ -0,0 +1,140 @@
+use ibc_core::client::context::client_state::ClientStateExecution;
+use ibc_core::client::context::prelude::ClientStateCommon;
+use ibc_core::client::context::ClientExecutionContext;
+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::primitives::proto::Any;
+use ibc_core::primitives::Timestamp;
+
+use crate::ConsensusState;
+
+use super::ClientState;
+
+impl ClientStateExecution for ClientState
+where
+ E: ClientExecutionContext,
+{
+ fn initialise(
+ &self,
+ ctx: &mut E,
+ client_id: &ClientId,
+ consensus_state: Any,
+ ) -> Result<(), ClientError> {
+ let latest_height = Height::min(0);
+
+ update_client_and_consensus_state(
+ ctx,
+ latest_height,
+ client_id,
+ self.clone(),
+ consensus_state.try_into()?,
+ )?;
+
+ Ok(())
+ }
+
+ fn update_state(
+ &self,
+ ctx: &mut E,
+ client_id: &ClientId,
+ header: Any,
+ ) -> Result, ClientError> {
+ let latest_height = ctx.client_state(client_id)?.latest_height().increment();
+
+ let new_client_state = ClientState { latest_height };
+
+ update_client_and_consensus_state(
+ ctx,
+ latest_height,
+ client_id,
+ new_client_state,
+ ConsensusState::default(),
+ )?;
+
+ Ok(vec![latest_height])
+ }
+
+ fn update_state_on_misbehaviour(
+ &self,
+ ctx: &mut E,
+ client_id: &ClientId,
+ client_message: Any,
+ ) -> Result<(), ClientError> {
+ Ok(())
+ }
+
+ fn update_state_on_upgrade(
+ &self,
+ ctx: &mut E,
+ client_id: &ClientId,
+ upgraded_client_state: Any,
+ upgraded_consensus_state: Any,
+ ) -> Result {
+ let latest_height = ctx.client_state(client_id)?.latest_height().increment();
+
+ let new_client_state = ClientState { latest_height };
+
+ update_client_and_consensus_state(
+ ctx,
+ latest_height,
+ client_id,
+ upgraded_client_state.try_into()?,
+ upgraded_consensus_state.try_into()?,
+ )?;
+
+ Ok(latest_height)
+ }
+
+ fn update_on_recovery(
+ &self,
+ ctx: &mut E,
+ subject_client_id: &ClientId,
+ substitute_client_state: Any,
+ substitute_consensus_state: Any,
+ ) -> Result<(), ClientError> {
+ let latest_height = ctx
+ .client_state(subject_client_id)?
+ .latest_height()
+ .increment();
+
+ let new_client_state = ClientState { latest_height };
+
+ update_client_and_consensus_state(
+ ctx,
+ latest_height,
+ subject_client_id,
+ substitute_client_state.try_into()?,
+ substitute_consensus_state.try_into()?,
+ )?;
+
+ Ok(())
+ }
+}
+
+fn update_client_and_consensus_state(
+ ctx: &mut E,
+ client_height: Height,
+ client_id: &ClientId,
+ client_state: E::ClientStateRef,
+ consensus_state: E::ConsensusStateRef,
+) -> Result<(), ClientError> {
+ ctx.store_consensus_state(
+ ClientConsensusStatePath::new(
+ client_id.clone(),
+ client_height.revision_number(),
+ client_height.revision_height(),
+ ),
+ consensus_state,
+ )?;
+ ctx.store_client_state(ClientStatePath::new(client_id.clone()), client_state)?;
+ ctx.store_update_meta(
+ client_id.clone(),
+ client_height,
+ Timestamp::none(),
+ Height::min(0),
+ )?;
+
+ Ok(())
+}
diff --git a/dummy-light-client/impls/src/client_state/mod.rs b/dummy-light-client/impls/src/client_state/mod.rs
new file mode 100644
index 00000000..2c0dd0fe
--- /dev/null
+++ b/dummy-light-client/impls/src/client_state/mod.rs
@@ -0,0 +1,44 @@
+pub mod common;
+pub mod execution;
+pub mod validation;
+
+use ibc_core::client::types::error::ClientError;
+use ibc_core::client::types::Height;
+use ibc_core::primitives::proto::{Any, Protobuf};
+
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug, PartialEq, derive_more::From)]
+pub struct ClientState {
+ latest_height: Height,
+}
+
+impl Protobuf for ClientState {}
+
+impl TryFrom for ClientState {
+ type Error = ClientError;
+
+ fn try_from(raw: Any) -> Result {
+ if raw.type_url != "/DummyClientState" {
+ return Err(ClientError::from("invalid type URL or empty value"));
+ }
+
+ let revision_number = u64::from_be_bytes(raw.value.try_into().unwrap());
+
+ Ok(Self {
+ latest_height: Height::min(revision_number),
+ })
+ }
+}
+
+impl From for Any {
+ fn from(client_state: ClientState) -> Self {
+ Self {
+ type_url: "/DummyClientState".to_string(),
+ value: client_state
+ .latest_height
+ .revision_number()
+ .to_be_bytes()
+ .to_vec(),
+ }
+ }
+}
diff --git a/dummy-light-client/impls/src/client_state/validation.rs b/dummy-light-client/impls/src/client_state/validation.rs
new file mode 100644
index 00000000..29e80150
--- /dev/null
+++ b/dummy-light-client/impls/src/client_state/validation.rs
@@ -0,0 +1,41 @@
+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;
+use ibc_core::primitives::proto::Any;
+
+use crate::ConsensusState;
+
+use super::ClientState;
+
+impl ClientStateValidation for ClientState
+where
+ V: ClientValidationContext,
+{
+ fn verify_client_message(
+ &self,
+ ctx: &V,
+ client_id: &ClientId,
+ client_message: Any,
+ ) -> Result<(), ClientError> {
+ Ok(())
+ }
+
+ fn check_for_misbehaviour(
+ &self,
+ ctx: &V,
+ client_id: &ClientId,
+ client_message: Any,
+ ) -> Result {
+ Ok(false)
+ }
+
+ fn status(&self, ctx: &V, client_id: &ClientId) -> Result {
+ Ok(Status::Active)
+ }
+
+ fn check_substitute(&self, ctx: &V, substitute_client_state: Any) -> Result<(), ClientError> {
+ Ok(())
+ }
+}
diff --git a/dummy-light-client/impls/src/consensus_state.rs b/dummy-light-client/impls/src/consensus_state.rs
new file mode 100644
index 00000000..058d1470
--- /dev/null
+++ b/dummy-light-client/impls/src/consensus_state.rs
@@ -0,0 +1,52 @@
+use ibc_core::client::context::consensus_state::ConsensusState as ConsensusStateTrait;
+use ibc_core::client::types::error::ClientError;
+use ibc_core::commitment_types::commitment::CommitmentRoot;
+use ibc_core::primitives::proto::{Any, Protobuf};
+use ibc_core::primitives::Timestamp;
+
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug, PartialEq, derive_more::From)]
+pub struct ConsensusState {
+ root: CommitmentRoot,
+}
+
+impl Default for ConsensusState {
+ fn default() -> Self {
+ Self {
+ root: CommitmentRoot::from(vec![]),
+ }
+ }
+}
+
+impl Protobuf for ConsensusState {}
+
+impl TryFrom for ConsensusState {
+ type Error = ClientError;
+
+ fn try_from(raw: Any) -> Result {
+ if raw.type_url != "/DummyConsensusState" || !raw.value.is_empty() {
+ return Err(ClientError::from("invalid type URL or empty value"));
+ }
+
+ Ok(Self::default())
+ }
+}
+
+impl From for Any {
+ fn from(consensus_state: ConsensusState) -> Self {
+ Self {
+ type_url: "/DummyConsensusState".to_string(),
+ value: vec![],
+ }
+ }
+}
+
+impl ConsensusStateTrait for ConsensusState {
+ fn root(&self) -> &CommitmentRoot {
+ &self.root
+ }
+
+ fn timestamp(&self) -> Timestamp {
+ Timestamp::none()
+ }
+}
diff --git a/dummy-light-client/impls/src/lib.rs b/dummy-light-client/impls/src/lib.rs
new file mode 100644
index 00000000..eb528a0f
--- /dev/null
+++ b/dummy-light-client/impls/src/lib.rs
@@ -0,0 +1,5 @@
+mod client_state;
+mod consensus_state;
+
+pub use client_state::*;
+pub use consensus_state::*;