use ibc_client_tendermint_types::{
ClientState as ClientStateType, ConsensusState as ConsensusStateType, Header as TmHeader,
};
use ibc_core_client::context::prelude::*;
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_primitives::prelude::*;
use ibc_primitives::proto::Any;
use super::ClientState;
impl<E> ClientStateExecution<E> for ClientState
where
E: ExtClientExecutionContext,
E::ClientStateRef: From<ClientStateType>,
ConsensusStateType: Convertible<E::ConsensusStateRef>,
<ConsensusStateType as TryFrom<E::ConsensusStateRef>>::Error: Into<ClientError>,
{
fn initialise(
&self,
ctx: &mut E,
client_id: &ClientId,
consensus_state: Any,
) -> Result<(), ClientError> {
initialise(self.inner(), ctx, client_id, consensus_state)
}
fn update_state(
&self,
ctx: &mut E,
client_id: &ClientId,
header: Any,
) -> Result<Vec<Height>, ClientError> {
update_state(self.inner(), ctx, client_id, header)
}
fn update_state_on_misbehaviour(
&self,
ctx: &mut E,
client_id: &ClientId,
client_message: Any,
) -> Result<(), ClientError> {
update_on_misbehaviour(self.inner(), ctx, client_id, client_message)
}
fn update_state_on_upgrade(
&self,
ctx: &mut E,
client_id: &ClientId,
upgraded_client_state: Any,
upgraded_consensus_state: Any,
) -> Result<Height, ClientError> {
update_on_upgrade(
self.inner(),
ctx,
client_id,
upgraded_client_state,
upgraded_consensus_state,
)
}
fn update_on_recovery(
&self,
ctx: &mut E,
subject_client_id: &ClientId,
substitute_client_state: Any,
substitute_consensus_state: Any,
) -> Result<(), ClientError> {
let subject_client_state = self.inner().clone();
update_on_recovery(
subject_client_state,
ctx,
subject_client_id,
substitute_client_state,
substitute_consensus_state,
)
}
}
pub fn initialise<E>(
client_state: &ClientStateType,
ctx: &mut E,
client_id: &ClientId,
consensus_state: Any,
) -> Result<(), ClientError>
where
E: ExtClientExecutionContext,
E::ClientStateRef: From<ClientStateType>,
ConsensusStateType: Convertible<E::ConsensusStateRef>,
{
let host_timestamp = ExtClientValidationContext::host_timestamp(ctx)?;
let host_height = ExtClientValidationContext::host_height(ctx)?;
let tm_consensus_state: ConsensusStateType = consensus_state.try_into()?;
ctx.store_client_state(
ClientStatePath::new(client_id.clone()),
client_state.clone().into(),
)?;
ctx.store_consensus_state(
ClientConsensusStatePath::new(
client_id.clone(),
client_state.latest_height.revision_number(),
client_state.latest_height.revision_height(),
),
tm_consensus_state.into(),
)?;
ctx.store_update_meta(
client_id.clone(),
client_state.latest_height,
host_timestamp,
host_height,
)?;
Ok(())
}
pub fn update_state<E>(
client_state: &ClientStateType,
ctx: &mut E,
client_id: &ClientId,
header: Any,
) -> Result<Vec<Height>, ClientError>
where
E: ExtClientExecutionContext,
E::ClientStateRef: From<ClientStateType>,
ConsensusStateType: Convertible<E::ConsensusStateRef>,
<ConsensusStateType as TryFrom<E::ConsensusStateRef>>::Error: Into<ClientError>,
{
let header = TmHeader::try_from(header)?;
let header_height = header.height();
prune_oldest_consensus_state(client_state, ctx, client_id)?;
let maybe_existing_consensus_state = {
let path_at_header_height = ClientConsensusStatePath::new(
client_id.clone(),
header_height.revision_number(),
header_height.revision_height(),
);
ctx.consensus_state(&path_at_header_height).ok()
};
if maybe_existing_consensus_state.is_some() {
} else {
let host_timestamp = ExtClientValidationContext::host_timestamp(ctx)?;
let host_height = ExtClientValidationContext::host_height(ctx)?;
let new_consensus_state = ConsensusStateType::from(header.clone());
let new_client_state = client_state.clone().with_header(header)?;
ctx.store_consensus_state(
ClientConsensusStatePath::new(
client_id.clone(),
header_height.revision_number(),
header_height.revision_height(),
),
new_consensus_state.into(),
)?;
ctx.store_client_state(
ClientStatePath::new(client_id.clone()),
new_client_state.into(),
)?;
ctx.store_update_meta(
client_id.clone(),
header_height,
host_timestamp,
host_height,
)?;
}
Ok(vec![header_height])
}
pub fn update_on_misbehaviour<E>(
client_state: &ClientStateType,
ctx: &mut E,
client_id: &ClientId,
_client_message: Any,
) -> Result<(), ClientError>
where
E: ExtClientExecutionContext,
E::ClientStateRef: From<ClientStateType>,
{
let frozen_client_state = client_state.clone().with_frozen_height(Height::min(0));
ctx.store_client_state(
ClientStatePath::new(client_id.clone()),
frozen_client_state.into(),
)?;
Ok(())
}
pub fn update_on_upgrade<E>(
client_state: &ClientStateType,
ctx: &mut E,
client_id: &ClientId,
upgraded_client_state: Any,
upgraded_consensus_state: Any,
) -> Result<Height, ClientError>
where
E: ExtClientExecutionContext,
E::ClientStateRef: From<ClientStateType>,
ConsensusStateType: Convertible<E::ConsensusStateRef>,
{
let mut upgraded_tm_client_state = ClientState::try_from(upgraded_client_state)?;
let upgraded_tm_cons_state: ConsensusStateType = upgraded_consensus_state.try_into()?;
upgraded_tm_client_state.0.zero_custom_fields();
let new_client_state = ClientStateType::new(
upgraded_tm_client_state.0.chain_id,
client_state.trust_level,
client_state.trusting_period,
upgraded_tm_client_state.0.unbonding_period,
client_state.max_clock_drift,
upgraded_tm_client_state.0.latest_height,
upgraded_tm_client_state.0.proof_specs,
upgraded_tm_client_state.0.upgrade_path,
client_state.allow_update,
)?;
let sentinel_root = b"sentinel_root".to_vec();
let new_consensus_state = ConsensusStateType::new(
sentinel_root.into(),
upgraded_tm_cons_state.timestamp(),
upgraded_tm_cons_state.next_validators_hash,
);
let latest_height = new_client_state.latest_height;
let host_timestamp = ExtClientValidationContext::host_timestamp(ctx)?;
let host_height = ExtClientValidationContext::host_height(ctx)?;
ctx.store_client_state(
ClientStatePath::new(client_id.clone()),
new_client_state.into(),
)?;
ctx.store_consensus_state(
ClientConsensusStatePath::new(
client_id.clone(),
latest_height.revision_number(),
latest_height.revision_height(),
),
new_consensus_state.into(),
)?;
ctx.store_update_meta(
client_id.clone(),
latest_height,
host_timestamp,
host_height,
)?;
Ok(latest_height)
}
pub fn prune_oldest_consensus_state<E>(
client_state: &ClientStateType,
ctx: &mut E,
client_id: &ClientId,
) -> Result<(), ClientError>
where
E: ClientExecutionContext + ExtClientValidationContext,
E::ClientStateRef: From<ClientStateType>,
ConsensusStateType: Convertible<E::ConsensusStateRef>,
<ConsensusStateType as TryFrom<E::ConsensusStateRef>>::Error: Into<ClientError>,
{
let mut heights = ctx.consensus_state_heights(client_id)?;
heights.sort();
for height in heights {
let client_consensus_state_path = ClientConsensusStatePath::new(
client_id.clone(),
height.revision_number(),
height.revision_height(),
);
let consensus_state = ctx.consensus_state(&client_consensus_state_path)?;
let tm_consensus_state: ConsensusStateType =
consensus_state.try_into().map_err(Into::into)?;
let host_timestamp =
ctx.host_timestamp()?
.into_tm_time()
.ok_or_else(|| ClientError::Other {
description: String::from("host timestamp is not a valid TM timestamp"),
})?;
let tm_consensus_state_timestamp = tm_consensus_state.timestamp();
let tm_consensus_state_expiry = (tm_consensus_state_timestamp
+ client_state.trusting_period)
.map_err(|_| ClientError::Other {
description: String::from(
"Timestamp overflow error occurred while attempting to parse TmConsensusState",
),
})?;
if tm_consensus_state_expiry > host_timestamp {
break;
}
ctx.delete_consensus_state(client_consensus_state_path)?;
ctx.delete_update_meta(client_id.clone(), height)?;
}
Ok(())
}
pub fn update_on_recovery<E>(
subject_client_state: ClientStateType,
ctx: &mut E,
subject_client_id: &ClientId,
substitute_client_state: Any,
substitute_consensus_state: Any,
) -> Result<(), ClientError>
where
E: ExtClientExecutionContext,
E::ClientStateRef: From<ClientStateType>,
ConsensusStateType: Convertible<E::ConsensusStateRef>,
{
let substitute_client_state = ClientState::try_from(substitute_client_state)?
.inner()
.clone();
let chain_id = substitute_client_state.chain_id;
let trusting_period = substitute_client_state.trusting_period;
let latest_height = substitute_client_state.latest_height;
let new_client_state = ClientStateType {
chain_id,
trusting_period,
latest_height,
frozen_height: None,
..subject_client_state
};
let host_timestamp = E::host_timestamp(ctx)?;
let host_height = E::host_height(ctx)?;
let tm_consensus_state: ConsensusStateType = substitute_consensus_state.try_into()?;
ctx.store_consensus_state(
ClientConsensusStatePath::new(
subject_client_id.clone(),
new_client_state.latest_height.revision_number(),
new_client_state.latest_height.revision_height(),
),
tm_consensus_state.into(),
)?;
ctx.store_client_state(
ClientStatePath::new(subject_client_id.clone()),
new_client_state.into(),
)?;
ctx.store_update_meta(
subject_client_id.clone(),
latest_height,
host_timestamp,
host_height,
)?;
Ok(())
}