ibc_client_tendermint/client_state/
update_client.rsuse ibc_client_tendermint_types::error::{IntoResult, TendermintClientError};
use ibc_client_tendermint_types::{ConsensusState as ConsensusStateType, Header as TmHeader};
use ibc_core_client::context::{Convertible, ExtClientValidationContext};
use ibc_core_client::types::error::ClientError;
use ibc_core_client::types::Height;
use ibc_core_host::types::error::IdentifierError;
use ibc_core_host::types::identifiers::{ChainId, ClientId};
use ibc_core_host::types::path::ClientConsensusStatePath;
use ibc_primitives::prelude::*;
use ibc_primitives::IntoHostTime;
use tendermint::crypto::Sha256;
use tendermint::merkle::MerkleHash;
use tendermint_light_client_verifier::options::Options;
use tendermint_light_client_verifier::types::{TrustedBlockState, UntrustedBlockState};
use tendermint_light_client_verifier::Verifier;
pub fn verify_header<V, H>(
ctx: &V,
header: &TmHeader,
client_id: &ClientId,
chain_id: &ChainId,
options: &Options,
verifier: &impl Verifier,
) -> Result<(), ClientError>
where
V: ExtClientValidationContext,
ConsensusStateType: Convertible<V::ConsensusStateRef>,
<ConsensusStateType as TryFrom<V::ConsensusStateRef>>::Error: Into<ClientError>,
H: MerkleHash + Sha256 + Default,
{
header.validate_basic::<H>()?;
header.verify_chain_id_version_matches_height(chain_id)?;
{
let trusted_state = {
let trusted_client_cons_state_path = ClientConsensusStatePath::new(
client_id.clone(),
header.trusted_height.revision_number(),
header.trusted_height.revision_height(),
);
let trusted_consensus_state: ConsensusStateType = ctx
.consensus_state(&trusted_client_cons_state_path)?
.try_into()
.map_err(Into::into)?;
header.check_trusted_next_validator_set::<H>(
&trusted_consensus_state.next_validators_hash,
)?;
TrustedBlockState {
chain_id: &chain_id.as_str().try_into().map_err(|e| {
IdentifierError::FailedToParse {
description: format!("chain ID `{chain_id}`: {e:?}"),
}
})?,
header_time: trusted_consensus_state.timestamp(),
height: header
.trusted_height
.revision_height()
.try_into()
.map_err(|_| ClientError::FailedToVerifyHeader {
description: TendermintClientError::InvalidHeaderHeight(
header.trusted_height.revision_height(),
)
.to_string(),
})?,
next_validators: &header.trusted_next_validator_set,
next_validators_hash: trusted_consensus_state.next_validators_hash,
}
};
let untrusted_state = UntrustedBlockState {
signed_header: &header.signed_header,
validators: &header.validator_set,
next_validators: None,
};
let now = ctx.host_timestamp()?.into_host_time()?;
verifier
.verify_update_header(untrusted_state, trusted_state, options, now)
.into_result()?;
}
Ok(())
}
pub fn check_for_misbehaviour_on_update<V>(
ctx: &V,
header: TmHeader,
client_id: &ClientId,
client_latest_height: &Height,
) -> Result<bool, ClientError>
where
V: ExtClientValidationContext,
ConsensusStateType: Convertible<V::ConsensusStateRef>,
<ConsensusStateType as TryFrom<V::ConsensusStateRef>>::Error: Into<ClientError>,
{
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 let Some(existing_consensus_state) = maybe_existing_consensus_state {
let existing_consensus_state: ConsensusStateType =
existing_consensus_state.try_into().map_err(Into::into)?;
let header_consensus_state = ConsensusStateType::from(header);
Ok(existing_consensus_state != header_consensus_state)
} else {
{
let maybe_prev_cs = ctx.prev_consensus_state(client_id, &header.height())?;
if let Some(prev_cs) = maybe_prev_cs {
let prev_cs: ConsensusStateType = prev_cs.try_into().map_err(Into::into)?;
if header.signed_header.header().time <= prev_cs.timestamp() {
return Ok(true);
}
}
}
if &header.height() < client_latest_height {
let maybe_next_cs = ctx.next_consensus_state(client_id, &header.height())?;
if let Some(next_cs) = maybe_next_cs {
let next_cs: ConsensusStateType = next_cs.try_into().map_err(Into::into)?;
if header.signed_header.header().time >= next_cs.timestamp() {
return Ok(true);
}
}
}
Ok(false)
}
}