Skip to content
This repository has been archived by the owner on Jun 24, 2024. It is now read-only.

Commit

Permalink
feat: implement client recovery (#125)
Browse files Browse the repository at this point in the history
* feat: implement client recovery methods

* chore: update basecoin rev

* feat: implement MigrateClientStore handler

* feat: integrate client recovery into CW client + test

* chore: rename RecoveryPrefix to MigrationPrefix

* misc: organize tests.rs + fix trusting_period + update ibc revison

* chore: update basecoin rev

* fix: make clippy happy

* fix: default trusting_period value

* deps: update basecoin rev

* fix: rename to migration_prefix
  • Loading branch information
Farhad-Shabani authored Apr 5, 2024
1 parent ee2f7d0 commit 5a64173
Show file tree
Hide file tree
Showing 20 changed files with 569 additions and 266 deletions.
102 changes: 51 additions & 51 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ thiserror = "1.0.38"
tracing = { version = "0.1.40", default-features = false }

# ibc depedenencies
ibc-core = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false, features = ["borsh","schema"] }
ibc-core-client = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false }
ibc-core-host-cosmos = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false }
ibc-client-tendermint = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false }
ibc-client-wasm-types = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false }
ibc-app-transfer = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false }
ibc-primitives = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false }
ibc-query = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false, features = ["schema"] }
ibc-testkit = { git = "https://github.com/cosmos/ibc-rs.git", rev = "d4c36554ba", default-features = false }
ibc-core = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false, features = ["borsh","schema"] }
ibc-core-client = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false }
ibc-core-host-cosmos = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false }
ibc-client-tendermint = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false }
ibc-client-wasm-types = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false }
ibc-app-transfer = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false }
ibc-primitives = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false }
ibc-query = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false, features = ["schema"] }
ibc-testkit = { git = "https://github.com/cosmos/ibc-rs.git", rev = "c579628c67", default-features = false }

# NOTE: `ibc-proto` is solely required by `sov-ibc-proto`. When needing Protobuf
# Rust types in the project, importing from their respective `ibc` type crates is
Expand Down
28 changes: 19 additions & 9 deletions clients/sov-celestia-cw/src/context/client_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ impl<'a, C: ClientType<'a>> ClientExecutionContext for Context<'a, C> {
_client_state_path: ClientStatePath,
client_state: Self::ClientStateMut,
) -> Result<(), ContextError> {
let key = ClientStatePath::leaf().into_bytes();
let prefixed_key = self.prefixed_key(ClientStatePath::leaf());

let encoded_client_state = self.encode_client_state(client_state)?;

self.insert(key, encoded_client_state);
self.insert(prefixed_key, encoded_client_state);

Ok(())
}
Expand All @@ -93,7 +93,7 @@ impl<'a, C: ClientType<'a>> ClientExecutionContext for Context<'a, C> {
consensus_state_path: ClientConsensusStatePath,
consensus_state: Self::ConsensusStateRef,
) -> Result<(), ContextError> {
let key = consensus_state_path.leaf().into_bytes();
let prefixed_key = self.prefixed_key(consensus_state_path.leaf());

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

Expand All @@ -103,7 +103,7 @@ impl<'a, C: ClientType<'a>> ClientExecutionContext for Context<'a, C> {

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

self.insert(key, encoded_wasm_consensus_state);
self.insert(prefixed_key, encoded_wasm_consensus_state);

Ok(())
}
Expand All @@ -112,7 +112,9 @@ impl<'a, C: ClientType<'a>> ClientExecutionContext for Context<'a, C> {
&mut self,
consensus_state_path: ClientConsensusStatePath,
) -> Result<(), ContextError> {
self.remove(consensus_state_path.leaf().into_bytes());
let prefixed_key = self.prefixed_key(consensus_state_path.leaf());

self.remove(prefixed_key);

Ok(())
}
Expand All @@ -126,15 +128,19 @@ impl<'a, C: ClientType<'a>> ClientExecutionContext for Context<'a, C> {
) -> 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(time_key, time_vec);
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(height_key, revision_height_vec);
self.insert(prefixed_height_key, revision_height_vec);

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

Expand All @@ -152,11 +158,15 @@ impl<'a, C: ClientType<'a>> ClientExecutionContext for Context<'a, C> {
) -> Result<(), ContextError> {
let time_key = self.client_update_time_key(&height);

self.remove(time_key);
let prefixed_time_key = self.prefixed_key(time_key);

self.remove(prefixed_time_key);

let height_key = self.client_update_height_key(&height);

self.remove(height_key);
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());

Expand Down
29 changes: 26 additions & 3 deletions clients/sov-celestia-cw/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ use ibc_core::primitives::proto::{Any, Protobuf};
use prost::Message;
use sov_celestia_client::types::codec::AnyCodec;

use crate::types::{parse_height, ClientType, ContractError, GenesisMetadata, HeightTravel};
use crate::types::{
parse_height, ClientType, ContractError, GenesisMetadata, HeightTravel, MigrationPrefix,
};

type Checksum = Vec<u8>;

Expand All @@ -29,6 +31,7 @@ pub struct Context<'a, C: ClientType<'a>> {
env: Env,
client_id: ClientId,
checksum: Option<Checksum>,
migration_prefix: MigrationPrefix,
client_type: std::marker::PhantomData<C>,
}

Expand All @@ -42,6 +45,7 @@ impl<'a, C: ClientType<'a>> Context<'a, C> {
env,
client_id,
checksum: None,
migration_prefix: MigrationPrefix::None,
client_type: std::marker::PhantomData::<C>,
})
}
Expand All @@ -55,6 +59,7 @@ impl<'a, C: ClientType<'a>> Context<'a, C> {
env,
client_id,
checksum: None,
migration_prefix: MigrationPrefix::None,
client_type: std::marker::PhantomData::<C>,
})
}
Expand All @@ -75,10 +80,28 @@ impl<'a, C: ClientType<'a>> Context<'a, C> {
self.checksum = Some(checksum);
}

pub fn set_subject_prefix(&mut self) {
self.migration_prefix = MigrationPrefix::Subject;
}

pub fn set_substitute_prefix(&mut self) {
self.migration_prefix = MigrationPrefix::Substitute;
}

pub fn prefixed_key(&self, key: impl AsRef<[u8]>) -> Vec<u8> {
let mut prefixed_key = Vec::new();
prefixed_key.extend_from_slice(self.migration_prefix.key());
prefixed_key.extend_from_slice(key.as_ref());

prefixed_key
}

pub fn retrieve(&self, key: impl AsRef<[u8]>) -> Result<Vec<u8>, ClientError> {
let prefixed_key = self.prefixed_key(key);

let value = self
.storage_ref()
.get(key.as_ref())
.get(prefixed_key.as_ref())
.ok_or(ClientError::Other {
description: "key not found".to_string(),
})?;
Expand Down Expand Up @@ -128,7 +151,7 @@ impl<'a, C: ClientType<'a>> Context<'a, C> {

pub fn client_update_time_key(&self, height: &Height) -> Vec<u8> {
let client_update_time_path = ClientUpdateTimePath::new(
self.client_id().clone(),
self.client_id(),
height.revision_number(),
height.revision_height(),
);
Expand Down
20 changes: 17 additions & 3 deletions clients/sov-celestia-cw/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ impl<'a, C: ClientType<'a>> Context<'a, C> {
pub fn sudo(&mut self, msg: SudoMsg) -> Result<Binary, ContractError> {
let client_id = self.client_id();

if let SudoMsg::MigrateClientStore(_) = msg {
self.set_subject_prefix();
};

let client_state = self.client_state(&client_id)?;

let result = match msg {
Expand Down Expand Up @@ -132,9 +136,19 @@ impl<'a, C: ClientType<'a>> Context<'a, C> {
ContractResult::success()
}
SudoMsg::MigrateClientStore(_) => {
return Err(ContractError::InvalidMsg(
"ibc-rs does no support this feature yet".to_string(),
));
self.set_substitute_prefix();
let substitute_client_state = self.client_state(&client_id)?;

self.set_subject_prefix();
client_state.check_substitute(self, substitute_client_state.clone().into())?;

client_state.update_on_recovery(
self,
&self.client_id(),
substitute_client_state.into(),
)?;

ContractResult::success()
}
};
Ok(to_json_binary(&result)?)
Expand Down
174 changes: 0 additions & 174 deletions clients/sov-celestia-cw/src/tests.rs

This file was deleted.

Loading

0 comments on commit 5a64173

Please sign in to comment.