ibc_client_tendermint_types/
misbehaviour.rs

1//! Defines the misbehaviour type for the tendermint light client
2
3use ibc_core_host_types::error::DecodingError;
4use ibc_core_host_types::identifiers::ClientId;
5use ibc_primitives::prelude::*;
6use ibc_proto::google::protobuf::Any;
7use ibc_proto::ibc::lightclients::tendermint::v1::Misbehaviour as RawMisbehaviour;
8use ibc_proto::Protobuf;
9use tendermint::crypto::Sha256;
10use tendermint::merkle::MerkleHash;
11
12use crate::error::TendermintClientError;
13use crate::header::Header;
14
15pub const TENDERMINT_MISBEHAVIOUR_TYPE_URL: &str = "/ibc.lightclients.tendermint.v1.Misbehaviour";
16
17/// Tendermint light client's misbehaviour type
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct Misbehaviour {
21    client_id: ClientId,
22    header1: Box<Header>,
23    header2: Box<Header>,
24}
25
26impl Misbehaviour {
27    pub fn new(client_id: ClientId, header1: Header, header2: Header) -> Self {
28        Self {
29            client_id,
30            header1: Box::new(header1),
31            header2: Box::new(header2),
32        }
33    }
34
35    pub fn client_id(&self) -> &ClientId {
36        &self.client_id
37    }
38
39    pub fn header1(&self) -> &Header {
40        &self.header1
41    }
42
43    pub fn header2(&self) -> &Header {
44        &self.header2
45    }
46
47    pub fn validate_basic<H: MerkleHash + Sha256 + Default>(
48        &self,
49    ) -> Result<(), TendermintClientError> {
50        self.header1.validate_basic::<H>()?;
51        self.header2.validate_basic::<H>()?;
52
53        if self.header1.signed_header.header.chain_id != self.header2.signed_header.header.chain_id
54        {
55            return Err(TendermintClientError::MismatchedHeaderChainIds {
56                expected: self.header1.signed_header.header.chain_id.to_string(),
57                actual: self.header2.signed_header.header.chain_id.to_string(),
58            });
59        }
60
61        if self.header1.height() < self.header2.height() {
62            return Err(
63                TendermintClientError::InsufficientMisbehaviourHeaderHeight {
64                    height_1: self.header1.height(),
65                    height_2: self.header2.height(),
66                },
67            );
68        }
69
70        Ok(())
71    }
72}
73
74impl Protobuf<RawMisbehaviour> for Misbehaviour {}
75
76impl TryFrom<RawMisbehaviour> for Misbehaviour {
77    type Error = DecodingError;
78
79    #[allow(deprecated)]
80    fn try_from(raw: RawMisbehaviour) -> Result<Self, Self::Error> {
81        let client_id = raw.client_id.parse()?;
82
83        let header1: Header = raw
84            .header_1
85            .ok_or_else(|| DecodingError::missing_raw_data("misbehaviour header1"))?
86            .try_into()?;
87
88        let header2: Header = raw
89            .header_2
90            .ok_or_else(|| DecodingError::missing_raw_data("misbehaviour header2"))?
91            .try_into()?;
92
93        Ok(Self::new(client_id, header1, header2))
94    }
95}
96
97impl From<Misbehaviour> for RawMisbehaviour {
98    fn from(value: Misbehaviour) -> Self {
99        #[allow(deprecated)]
100        RawMisbehaviour {
101            client_id: value.client_id.to_string(),
102            header_1: Some((*value.header1).into()),
103            header_2: Some((*value.header2).into()),
104        }
105    }
106}
107
108impl Protobuf<Any> for Misbehaviour {}
109
110impl TryFrom<Any> for Misbehaviour {
111    type Error = DecodingError;
112
113    fn try_from(raw: Any) -> Result<Self, Self::Error> {
114        if let TENDERMINT_MISBEHAVIOUR_TYPE_URL = raw.type_url.as_str() {
115            Protobuf::<RawMisbehaviour>::decode(raw.value.as_ref()).map_err(Into::into)
116        } else {
117            Err(DecodingError::MismatchedResourceName {
118                expected: TENDERMINT_MISBEHAVIOUR_TYPE_URL.to_string(),
119                actual: raw.type_url,
120            })
121        }
122    }
123}
124
125impl From<Misbehaviour> for Any {
126    fn from(misbehaviour: Misbehaviour) -> Self {
127        Any {
128            type_url: TENDERMINT_MISBEHAVIOUR_TYPE_URL.to_string(),
129            value: Protobuf::<RawMisbehaviour>::encode_vec(misbehaviour),
130        }
131    }
132}
133
134impl core::fmt::Display for Misbehaviour {
135    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
136        write!(
137            f,
138            "{} h1: {}-{} h2: {}-{}",
139            self.client_id,
140            self.header1.height(),
141            self.header1.trusted_height,
142            self.header2.height(),
143            self.header2.trusted_height,
144        )
145    }
146}