ibc_client_tendermint/client_state/
misbehaviour.rs1use ibc_client_tendermint_types::error::{IntoResult, TendermintClientError};
2use ibc_client_tendermint_types::{
3 ConsensusState as ConsensusStateType, Header as TmHeader, Misbehaviour as TmMisbehaviour,
4};
5use ibc_core_client::context::{Convertible, ExtClientValidationContext};
6use ibc_core_client::types::error::ClientError;
7use ibc_core_host::types::error::IdentifierError;
8use ibc_core_host::types::identifiers::{ChainId, ClientId};
9use ibc_core_host::types::path::ClientConsensusStatePath;
10use ibc_primitives::prelude::*;
11use ibc_primitives::{IntoHostTime, IntoTimestamp, Timestamp};
12use tendermint::crypto::Sha256;
13use tendermint::merkle::MerkleHash;
14use tendermint::{Hash, Time};
15use tendermint_light_client_verifier::options::Options;
16use tendermint_light_client_verifier::Verifier;
17
18use crate::types::Header;
19
20pub fn verify_misbehaviour<V, H>(
23 ctx: &V,
24 misbehaviour: &TmMisbehaviour,
25 client_id: &ClientId,
26 chain_id: &ChainId,
27 options: &Options,
28 verifier: &impl Verifier,
29) -> Result<(), ClientError>
30where
31 V: ExtClientValidationContext,
32 ConsensusStateType: Convertible<V::ConsensusStateRef>,
33 <ConsensusStateType as TryFrom<V::ConsensusStateRef>>::Error: Into<ClientError>,
34 H: MerkleHash + Sha256 + Default,
35{
36 misbehaviour.validate_basic::<H>()?;
37
38 let header_1 = misbehaviour.header1();
39 let trusted_consensus_state_1: ConsensusStateType = {
40 let consensus_state_path = ClientConsensusStatePath::new(
41 client_id.clone(),
42 header_1.trusted_height.revision_number(),
43 header_1.trusted_height.revision_height(),
44 );
45 let consensus_state = ctx.consensus_state(&consensus_state_path)?;
46
47 consensus_state.try_into().map_err(Into::into)?
48 };
49
50 let header_2 = misbehaviour.header2();
51 let trusted_consensus_state_2: ConsensusStateType = {
52 let consensus_state_path = ClientConsensusStatePath::new(
53 client_id.clone(),
54 header_2.trusted_height.revision_number(),
55 header_2.trusted_height.revision_height(),
56 );
57 let consensus_state = ctx.consensus_state(&consensus_state_path)?;
58
59 consensus_state.try_into().map_err(Into::into)?
60 };
61
62 let current_timestamp = ctx.host_timestamp()?;
63
64 verify_misbehaviour_header::<H>(
65 header_1,
66 chain_id,
67 options,
68 trusted_consensus_state_1.timestamp(),
69 trusted_consensus_state_1.next_validators_hash,
70 current_timestamp,
71 verifier,
72 )?;
73 verify_misbehaviour_header::<H>(
74 header_2,
75 chain_id,
76 options,
77 trusted_consensus_state_2.timestamp(),
78 trusted_consensus_state_2.next_validators_hash,
79 current_timestamp,
80 verifier,
81 )
82}
83
84pub fn verify_misbehaviour_header<H>(
85 header: &TmHeader,
86 chain_id: &ChainId,
87 options: &Options,
88 trusted_time: Time,
89 trusted_next_validator_hash: Hash,
90 current_timestamp: Timestamp,
91 verifier: &impl Verifier,
92) -> Result<(), ClientError>
93where
94 H: MerkleHash + Sha256 + Default,
95{
96 header.check_trusted_next_validator_set::<H>(&trusted_next_validator_hash)?;
98
99 {
101 let trusted_timestamp = trusted_time.into_timestamp()?;
102
103 let duration_since_consensus_state =
104 current_timestamp.duration_since(&trusted_timestamp).ok_or(
105 ClientError::InvalidConsensusStateTimestamp(trusted_timestamp),
106 )?;
107
108 if duration_since_consensus_state >= options.trusting_period {
109 return Err(TendermintClientError::InsufficientTrustingPeriod {
110 duration_since_consensus_state,
111 trusting_period: options.trusting_period,
112 }
113 .into());
114 }
115 }
116
117 let untrusted_state = header.as_untrusted_block_state();
119
120 let tm_chain_id =
121 &chain_id
122 .as_str()
123 .try_into()
124 .map_err(|e| IdentifierError::FailedToParse {
125 description: format!("chain ID `{chain_id}`: {e:?}"),
126 })?;
127
128 let trusted_state =
129 header.as_trusted_block_state(tm_chain_id, trusted_time, trusted_next_validator_hash)?;
130
131 let current_timestamp = current_timestamp.into_host_time()?;
132
133 verifier
134 .verify_misbehaviour_header(untrusted_state, trusted_state, options, current_timestamp)
135 .into_result()?;
136
137 Ok(())
138}
139
140pub fn check_for_misbehaviour_on_misbehavior(
141 header_1: &Header,
142 header_2: &Header,
143) -> Result<bool, ClientError> {
144 if header_1.height() == header_2.height() {
145 Ok(header_1.signed_header.commit.block_id.hash
150 != header_2.signed_header.commit.block_id.hash)
151 } else {
152 Ok(header_1.signed_header.header.time <= header_2.signed_header.header.time)
157 }
158}